diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ef30715e..3ae04808a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,4 +53,25 @@ jobs: - name: Run tests run: ./scripts/test + examples: + name: examples + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: | + 8 + 17 + cache: gradle + - name: Set up Gradle + uses: gradle/gradle-build-action@v2 + - env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + run: | + ./gradlew :openai-java-example:run diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 554e34bbe..f81bf9923 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.30.0" + ".": "0.31.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index e7dc5cc33..9790b6efb 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ -configured_endpoints: 65 +configured_endpoints: 72 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-5d30684c3118d049682ea30cdb4dbef39b97d51667da484689193dc40162af32.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 941bb6a63..4b49c42c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## 0.31.0 (2025-03-04) + +Full Changelog: [v0.30.0...v0.31.0](https://github.com/openai/openai-java/compare/v0.30.0...v0.31.0) + +### Features + +* **client:** add file upload endpoints ([#268](https://github.com/openai/openai-java/issues/268)) ([456274d](https://github.com/openai/openai-java/commit/456274d2dc572ad4711d2fc3640c7e01aa08f4b8)) +* **client:** allow configuring timeouts granularly ([#266](https://github.com/openai/openai-java/issues/266)) ([c3bc6e4](https://github.com/openai/openai-java/commit/c3bc6e4fb9717c7f6146f864ce3cc2451619c9f0)) + + +### Chores + +* **internal:** refactor `ErrorHandlingTest` ([#264](https://github.com/openai/openai-java/issues/264)) ([2472f85](https://github.com/openai/openai-java/commit/2472f859b49f49cf14539e787cf15e8f863f5dac)) +* **internal:** run example files in CI ([#271](https://github.com/openai/openai-java/issues/271)) ([8da7851](https://github.com/openai/openai-java/commit/8da785184c3e039ea4b9d2b26c6d14b607291750)) + + +### Documentation + +* add raw response readme documentation ([#269](https://github.com/openai/openai-java/issues/269)) ([2839903](https://github.com/openai/openai-java/commit/28399031c60eed8899475c5d1d38677c2eaa2284)) +* update URLs from stainlessapi.com to stainless.com ([#259](https://github.com/openai/openai-java/issues/259)) ([b824bf3](https://github.com/openai/openai-java/commit/b824bf39a44251e15c7c8b5b14bc6cbf9133dff5)) + ## 0.30.0 (2025-02-27) Full Changelog: [v0.29.0...v0.30.0](https://github.com/openai/openai-java/compare/v0.29.0...v0.30.0) diff --git a/README.md b/README.md index f37bcbc53..f732fcf8d 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,8 @@ -[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/0.30.0) -[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/0.30.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/0.30.0) +[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/0.31.0) +[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/0.31.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/0.31.0) @@ -25,7 +25,7 @@ The REST API documentation can be found on [platform.openai.com](https://platfor ### Gradle ```kotlin -implementation("com.openai:openai-java:0.30.0") +implementation("com.openai:openai-java:0.31.0") ``` ### Maven @@ -34,7 +34,7 @@ implementation("com.openai:openai-java:0.30.0") com.openai openai-java - 0.30.0 + 0.31.0 ``` @@ -314,6 +314,37 @@ try (HttpResponse response = client.files().content(params)) { } ``` +## Raw responses + +The SDK defines methods that deserialize responses into instances of Java classes. However, these methods don't provide access to the response headers, status code, or the raw response body. + +To access this data, prefix any HTTP method call on a client or service with `withRawResponse()`: + +```java +import com.openai.core.http.Headers; +import com.openai.core.http.HttpResponseFor; +import com.openai.models.ChatCompletion; +import com.openai.models.ChatCompletionCreateParams; +import com.openai.models.ChatModel; + +ChatCompletionCreateParams params = ChatCompletionCreateParams.builder() + .addUserMessage("Say this is a test") + .model(ChatModel.O3_MINI) + .build(); +HttpResponseFor chatCompletion = client.chat().completions().withRawResponse().create(params); + +int statusCode = chatCompletion.statusCode(); +Headers headers = chatCompletion.headers(); +``` + +You can still deserialize the response into an instance of a Java class if needed: + +```java +import com.openai.models.ChatCompletion; + +ChatCompletion parsedChatCompletion = chatCompletion.parse(); +``` + ## Error handling The SDK throws custom unchecked exception types: diff --git a/SECURITY.md b/SECURITY.md index c54acaf33..3b3bd8a66 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,9 +2,9 @@ ## Reporting Security Issues -This SDK is generated by [Stainless Software Inc](http://stainlessapi.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. +This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. -To report a security issue, please contact the Stainless team at security@stainlessapi.com. +To report a security issue, please contact the Stainless team at security@stainless.com. ## Responsible Disclosure diff --git a/build.gradle.kts b/build.gradle.kts index 52fd836f1..d91f6a4a8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ repositories { allprojects { group = "com.openai" - version = "0.30.0" // x-release-please-version + version = "0.31.0" // x-release-please-version } subprojects { diff --git a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OkHttpClient.kt b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OkHttpClient.kt index 409bcf9de..8fc863a5c 100644 --- a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OkHttpClient.kt +++ b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OkHttpClient.kt @@ -1,6 +1,7 @@ package com.openai.client.okhttp import com.openai.core.RequestOptions +import com.openai.core.Timeout import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.HttpClient @@ -88,13 +89,12 @@ private constructor(private val okHttpClient: okhttp3.OkHttpClient, private val ) } - val timeout = requestOptions.timeout - if (timeout != null) { + requestOptions.timeout?.let { clientBuilder - .connectTimeout(timeout) - .readTimeout(timeout) - .writeTimeout(timeout) - .callTimeout(if (timeout.seconds == 0L) timeout else timeout.plusSeconds(30)) + .connectTimeout(it.connect()) + .readTimeout(it.read()) + .writeTimeout(it.write()) + .callTimeout(it.request()) } val client = clientBuilder.build() @@ -195,23 +195,24 @@ private constructor(private val okHttpClient: okhttp3.OkHttpClient, private val class Builder internal constructor() { private var baseUrl: HttpUrl? = null - // The default timeout is 10 minutes. - private var timeout: Duration = Duration.ofSeconds(600) + private var timeout: Timeout = Timeout.default() private var proxy: Proxy? = null fun baseUrl(baseUrl: String) = apply { this.baseUrl = baseUrl.toHttpUrl() } - fun timeout(timeout: Duration) = apply { this.timeout = timeout } + fun timeout(timeout: Timeout) = apply { this.timeout = timeout } + + fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build()) fun proxy(proxy: Proxy?) = apply { this.proxy = proxy } fun build(): OkHttpClient = OkHttpClient( okhttp3.OkHttpClient.Builder() - .connectTimeout(timeout) - .readTimeout(timeout) - .writeTimeout(timeout) - .callTimeout(if (timeout.seconds == 0L) timeout else timeout.plusSeconds(30)) + .connectTimeout(timeout.connect()) + .readTimeout(timeout.read()) + .writeTimeout(timeout.write()) + .callTimeout(timeout.request()) .proxy(proxy) .build(), checkRequired("baseUrl", baseUrl), diff --git a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt index 41abe1c26..a0c4a83bc 100644 --- a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt +++ b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt @@ -7,6 +7,7 @@ import com.openai.azure.AzureOpenAIServiceVersion import com.openai.client.OpenAIClient import com.openai.client.OpenAIClientImpl import com.openai.core.ClientOptions +import com.openai.core.Timeout import com.openai.core.http.Headers import com.openai.core.http.QueryParams import com.openai.credential.Credential @@ -30,8 +31,7 @@ class OpenAIOkHttpClient private constructor() { private var clientOptions: ClientOptions.Builder = ClientOptions.builder() private var baseUrl: String = ClientOptions.PRODUCTION_URL - // The default timeout for the client is 10 minutes. - private var timeout: Duration = Duration.ofSeconds(600) + private var timeout: Timeout = Timeout.default() private var proxy: Proxy? = null fun baseUrl(baseUrl: String) = apply { @@ -127,7 +127,19 @@ class OpenAIOkHttpClient private constructor() { clientOptions.removeAllQueryParams(keys) } - fun timeout(timeout: Duration) = apply { this.timeout = timeout } + fun timeout(timeout: Timeout) = apply { + clientOptions.timeout(timeout) + this.timeout = timeout + } + + /** + * Sets the maximum time allowed for a complete HTTP call, not including retries. + * + * See [Timeout.request] for more details. + * + * For fine-grained control, pass a [Timeout] object. + */ + fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build()) fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) } diff --git a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt index 24b0d9ab5..13163b9fe 100644 --- a/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt +++ b/openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt @@ -7,6 +7,7 @@ import com.openai.azure.AzureOpenAIServiceVersion import com.openai.client.OpenAIClientAsync import com.openai.client.OpenAIClientAsyncImpl import com.openai.core.ClientOptions +import com.openai.core.Timeout import com.openai.core.http.Headers import com.openai.core.http.QueryParams import com.openai.credential.Credential @@ -30,8 +31,7 @@ class OpenAIOkHttpClientAsync private constructor() { private var clientOptions: ClientOptions.Builder = ClientOptions.builder() private var baseUrl: String = ClientOptions.PRODUCTION_URL - // The default timeout for the client is 10 minutes. - private var timeout: Duration = Duration.ofSeconds(600) + private var timeout: Timeout = Timeout.default() private var proxy: Proxy? = null fun baseUrl(baseUrl: String) = apply { @@ -127,7 +127,19 @@ class OpenAIOkHttpClientAsync private constructor() { clientOptions.removeAllQueryParams(keys) } - fun timeout(timeout: Duration) = apply { this.timeout = timeout } + fun timeout(timeout: Timeout) = apply { + clientOptions.timeout(timeout) + this.timeout = timeout + } + + /** + * Sets the maximum time allowed for a complete HTTP call, not including retries. + * + * See [Timeout.request] for more details. + * + * For fine-grained control, pass a [Timeout] object. + */ + fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build()) fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) } diff --git a/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClient.kt b/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClient.kt index 7aa192b94..a1390a56f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClient.kt +++ b/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClient.kt @@ -2,6 +2,7 @@ package com.openai.client +import com.openai.services.blocking.AudioService import com.openai.services.blocking.BatchService import com.openai.services.blocking.BetaService import com.openai.services.blocking.ChatService @@ -38,6 +39,11 @@ interface OpenAIClient { */ fun async(): OpenAIClientAsync + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun completions(): CompletionService fun chat(): ChatService @@ -48,6 +54,8 @@ interface OpenAIClient { fun images(): ImageService + fun audio(): AudioService + fun moderations(): ModerationService fun models(): ModelService @@ -72,4 +80,32 @@ interface OpenAIClient { * method. */ fun close() + + /** A view of [OpenAIClient] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun completions(): CompletionService.WithRawResponse + + fun chat(): ChatService.WithRawResponse + + fun embeddings(): EmbeddingService.WithRawResponse + + fun files(): FileService.WithRawResponse + + fun images(): ImageService.WithRawResponse + + fun audio(): AudioService.WithRawResponse + + fun moderations(): ModerationService.WithRawResponse + + fun models(): ModelService.WithRawResponse + + fun fineTuning(): FineTuningService.WithRawResponse + + fun beta(): BetaService.WithRawResponse + + fun batches(): BatchService.WithRawResponse + + fun uploads(): UploadService.WithRawResponse + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsync.kt b/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsync.kt index 1c1b63781..12679360b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsync.kt @@ -2,6 +2,7 @@ package com.openai.client +import com.openai.services.async.AudioServiceAsync import com.openai.services.async.BatchServiceAsync import com.openai.services.async.BetaServiceAsync import com.openai.services.async.ChatServiceAsync @@ -38,6 +39,11 @@ interface OpenAIClientAsync { */ fun sync(): OpenAIClient + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun completions(): CompletionServiceAsync fun chat(): ChatServiceAsync @@ -48,6 +54,8 @@ interface OpenAIClientAsync { fun images(): ImageServiceAsync + fun audio(): AudioServiceAsync + fun moderations(): ModerationServiceAsync fun models(): ModelServiceAsync @@ -72,4 +80,32 @@ interface OpenAIClientAsync { * method. */ fun close() + + /** A view of [OpenAIClientAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun completions(): CompletionServiceAsync.WithRawResponse + + fun chat(): ChatServiceAsync.WithRawResponse + + fun embeddings(): EmbeddingServiceAsync.WithRawResponse + + fun files(): FileServiceAsync.WithRawResponse + + fun images(): ImageServiceAsync.WithRawResponse + + fun audio(): AudioServiceAsync.WithRawResponse + + fun moderations(): ModerationServiceAsync.WithRawResponse + + fun models(): ModelServiceAsync.WithRawResponse + + fun fineTuning(): FineTuningServiceAsync.WithRawResponse + + fun beta(): BetaServiceAsync.WithRawResponse + + fun batches(): BatchServiceAsync.WithRawResponse + + fun uploads(): UploadServiceAsync.WithRawResponse + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsyncImpl.kt index d69c816fc..cff9f827a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsyncImpl.kt @@ -4,6 +4,8 @@ package com.openai.client import com.openai.core.ClientOptions import com.openai.core.getPackageVersion +import com.openai.services.async.AudioServiceAsync +import com.openai.services.async.AudioServiceAsyncImpl import com.openai.services.async.BatchServiceAsync import com.openai.services.async.BatchServiceAsyncImpl import com.openai.services.async.BetaServiceAsync @@ -40,6 +42,10 @@ class OpenAIClientAsyncImpl(private val clientOptions: ClientOptions) : OpenAICl // Pass the original clientOptions so that this client sets its own User-Agent. private val sync: OpenAIClient by lazy { OpenAIClientImpl(clientOptions) } + private val withRawResponse: OpenAIClientAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + private val completions: CompletionServiceAsync by lazy { CompletionServiceAsyncImpl(clientOptionsWithUserAgent) } @@ -56,6 +62,10 @@ class OpenAIClientAsyncImpl(private val clientOptions: ClientOptions) : OpenAICl ImageServiceAsyncImpl(clientOptionsWithUserAgent) } + private val audio: AudioServiceAsync by lazy { + AudioServiceAsyncImpl(clientOptionsWithUserAgent) + } + private val moderations: ModerationServiceAsync by lazy { ModerationServiceAsyncImpl(clientOptionsWithUserAgent) } @@ -80,6 +90,8 @@ class OpenAIClientAsyncImpl(private val clientOptions: ClientOptions) : OpenAICl override fun sync(): OpenAIClient = sync + override fun withRawResponse(): OpenAIClientAsync.WithRawResponse = withRawResponse + override fun completions(): CompletionServiceAsync = completions override fun chat(): ChatServiceAsync = chat @@ -90,6 +102,8 @@ class OpenAIClientAsyncImpl(private val clientOptions: ClientOptions) : OpenAICl override fun images(): ImageServiceAsync = images + override fun audio(): AudioServiceAsync = audio + override fun moderations(): ModerationServiceAsync = moderations override fun models(): ModelServiceAsync = models @@ -103,4 +117,80 @@ class OpenAIClientAsyncImpl(private val clientOptions: ClientOptions) : OpenAICl override fun uploads(): UploadServiceAsync = uploads override fun close() = clientOptions.httpClient.close() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + OpenAIClientAsync.WithRawResponse { + + private val completions: CompletionServiceAsync.WithRawResponse by lazy { + CompletionServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val chat: ChatServiceAsync.WithRawResponse by lazy { + ChatServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val embeddings: EmbeddingServiceAsync.WithRawResponse by lazy { + EmbeddingServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val files: FileServiceAsync.WithRawResponse by lazy { + FileServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val images: ImageServiceAsync.WithRawResponse by lazy { + ImageServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val audio: AudioServiceAsync.WithRawResponse by lazy { + AudioServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val moderations: ModerationServiceAsync.WithRawResponse by lazy { + ModerationServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val models: ModelServiceAsync.WithRawResponse by lazy { + ModelServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val fineTuning: FineTuningServiceAsync.WithRawResponse by lazy { + FineTuningServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val beta: BetaServiceAsync.WithRawResponse by lazy { + BetaServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val batches: BatchServiceAsync.WithRawResponse by lazy { + BatchServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val uploads: UploadServiceAsync.WithRawResponse by lazy { + UploadServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun completions(): CompletionServiceAsync.WithRawResponse = completions + + override fun chat(): ChatServiceAsync.WithRawResponse = chat + + override fun embeddings(): EmbeddingServiceAsync.WithRawResponse = embeddings + + override fun files(): FileServiceAsync.WithRawResponse = files + + override fun images(): ImageServiceAsync.WithRawResponse = images + + override fun audio(): AudioServiceAsync.WithRawResponse = audio + + override fun moderations(): ModerationServiceAsync.WithRawResponse = moderations + + override fun models(): ModelServiceAsync.WithRawResponse = models + + override fun fineTuning(): FineTuningServiceAsync.WithRawResponse = fineTuning + + override fun beta(): BetaServiceAsync.WithRawResponse = beta + + override fun batches(): BatchServiceAsync.WithRawResponse = batches + + override fun uploads(): UploadServiceAsync.WithRawResponse = uploads + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientImpl.kt b/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientImpl.kt index bc2e89270..4898af55f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientImpl.kt @@ -4,6 +4,8 @@ package com.openai.client import com.openai.core.ClientOptions import com.openai.core.getPackageVersion +import com.openai.services.blocking.AudioService +import com.openai.services.blocking.AudioServiceImpl import com.openai.services.blocking.BatchService import com.openai.services.blocking.BatchServiceImpl import com.openai.services.blocking.BetaService @@ -40,6 +42,10 @@ class OpenAIClientImpl(private val clientOptions: ClientOptions) : OpenAIClient // Pass the original clientOptions so that this client sets its own User-Agent. private val async: OpenAIClientAsync by lazy { OpenAIClientAsyncImpl(clientOptions) } + private val withRawResponse: OpenAIClient.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + private val completions: CompletionService by lazy { CompletionServiceImpl(clientOptionsWithUserAgent) } @@ -54,6 +60,8 @@ class OpenAIClientImpl(private val clientOptions: ClientOptions) : OpenAIClient private val images: ImageService by lazy { ImageServiceImpl(clientOptionsWithUserAgent) } + private val audio: AudioService by lazy { AudioServiceImpl(clientOptionsWithUserAgent) } + private val moderations: ModerationService by lazy { ModerationServiceImpl(clientOptionsWithUserAgent) } @@ -72,6 +80,8 @@ class OpenAIClientImpl(private val clientOptions: ClientOptions) : OpenAIClient override fun async(): OpenAIClientAsync = async + override fun withRawResponse(): OpenAIClient.WithRawResponse = withRawResponse + override fun completions(): CompletionService = completions override fun chat(): ChatService = chat @@ -82,6 +92,8 @@ class OpenAIClientImpl(private val clientOptions: ClientOptions) : OpenAIClient override fun images(): ImageService = images + override fun audio(): AudioService = audio + override fun moderations(): ModerationService = moderations override fun models(): ModelService = models @@ -95,4 +107,80 @@ class OpenAIClientImpl(private val clientOptions: ClientOptions) : OpenAIClient override fun uploads(): UploadService = uploads override fun close() = clientOptions.httpClient.close() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + OpenAIClient.WithRawResponse { + + private val completions: CompletionService.WithRawResponse by lazy { + CompletionServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val chat: ChatService.WithRawResponse by lazy { + ChatServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val embeddings: EmbeddingService.WithRawResponse by lazy { + EmbeddingServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val files: FileService.WithRawResponse by lazy { + FileServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val images: ImageService.WithRawResponse by lazy { + ImageServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val audio: AudioService.WithRawResponse by lazy { + AudioServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val moderations: ModerationService.WithRawResponse by lazy { + ModerationServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val models: ModelService.WithRawResponse by lazy { + ModelServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val fineTuning: FineTuningService.WithRawResponse by lazy { + FineTuningServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val beta: BetaService.WithRawResponse by lazy { + BetaServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val batches: BatchService.WithRawResponse by lazy { + BatchServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val uploads: UploadService.WithRawResponse by lazy { + UploadServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun completions(): CompletionService.WithRawResponse = completions + + override fun chat(): ChatService.WithRawResponse = chat + + override fun embeddings(): EmbeddingService.WithRawResponse = embeddings + + override fun files(): FileService.WithRawResponse = files + + override fun images(): ImageService.WithRawResponse = images + + override fun audio(): AudioService.WithRawResponse = audio + + override fun moderations(): ModerationService.WithRawResponse = moderations + + override fun models(): ModelService.WithRawResponse = models + + override fun fineTuning(): FineTuningService.WithRawResponse = fineTuning + + override fun beta(): BetaService.WithRawResponse = beta + + override fun batches(): BatchService.WithRawResponse = batches + + override fun uploads(): UploadService.WithRawResponse = uploads + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/core/Check.kt b/openai-java-core/src/main/kotlin/com/openai/core/Check.kt index 1ff8c724b..6b58f9c77 100644 --- a/openai-java-core/src/main/kotlin/com/openai/core/Check.kt +++ b/openai-java-core/src/main/kotlin/com/openai/core/Check.kt @@ -5,6 +5,18 @@ package com.openai.core fun checkRequired(name: String, value: T?): T = checkNotNull(value) { "`$name` is required, but was not set" } +@JvmSynthetic +internal fun checkKnown(name: String, value: JsonField): T = + value.asKnown().orElseThrow { + IllegalStateException("`$name` is not a known type: ${value.javaClass.simpleName}") + } + +@JvmSynthetic +internal fun checkKnown(name: String, value: MultipartField): T = + value.value.asKnown().orElseThrow { + IllegalStateException("`$name` is not a known type: ${value.javaClass.simpleName}") + } + @JvmSynthetic internal fun checkLength(name: String, value: String, length: Int): String = value.also { diff --git a/openai-java-core/src/main/kotlin/com/openai/core/ClientOptions.kt b/openai-java-core/src/main/kotlin/com/openai/core/ClientOptions.kt index 7b8e6ecd7..744f143f9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/core/ClientOptions.kt +++ b/openai-java-core/src/main/kotlin/com/openai/core/ClientOptions.kt @@ -30,6 +30,7 @@ private constructor( @get:JvmName("headers") val headers: Headers, @get:JvmName("queryParams") val queryParams: QueryParams, @get:JvmName("responseValidation") val responseValidation: Boolean, + @get:JvmName("timeout") val timeout: Timeout, @get:JvmName("maxRetries") val maxRetries: Int, @get:JvmName("credential") val credential: Credential, @get:JvmName("organization") val organization: String?, @@ -58,6 +59,7 @@ private constructor( private var headers: Headers.Builder = Headers.builder() private var queryParams: QueryParams.Builder = QueryParams.builder() private var responseValidation: Boolean = false + private var timeout: Timeout = Timeout.default() private var maxRetries: Int = 2 private var credential: Credential? = null private var azureServiceVersion: AzureOpenAIServiceVersion? = null @@ -74,6 +76,7 @@ private constructor( headers = clientOptions.headers.toBuilder() queryParams = clientOptions.queryParams.toBuilder() responseValidation = clientOptions.responseValidation + timeout = clientOptions.timeout maxRetries = clientOptions.maxRetries credential = clientOptions.credential organization = clientOptions.organization @@ -96,6 +99,8 @@ private constructor( this.responseValidation = responseValidation } + fun timeout(timeout: Timeout) = apply { this.timeout = timeout } + fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries } fun apiKey(apiKey: String) = apply { @@ -299,6 +304,7 @@ private constructor( headers.build(), queryParams.build(), responseValidation, + timeout, maxRetries, credential, organization, diff --git a/openai-java-core/src/main/kotlin/com/openai/core/HttpRequestBodies.kt b/openai-java-core/src/main/kotlin/com/openai/core/HttpRequestBodies.kt deleted file mode 100644 index 253e7e531..000000000 --- a/openai-java-core/src/main/kotlin/com/openai/core/HttpRequestBodies.kt +++ /dev/null @@ -1,108 +0,0 @@ -@file:JvmName("HttpRequestBodies") - -package com.openai.core - -import com.fasterxml.jackson.databind.json.JsonMapper -import com.openai.core.http.HttpRequestBody -import com.openai.errors.OpenAIException -import java.io.ByteArrayOutputStream -import java.io.OutputStream -import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder - -@JvmSynthetic -internal inline fun json(jsonMapper: JsonMapper, value: T): HttpRequestBody { - return object : HttpRequestBody { - private var cachedBytes: ByteArray? = null - - private fun serialize(): ByteArray { - if (cachedBytes != null) return cachedBytes!! - - val buffer = ByteArrayOutputStream() - try { - jsonMapper.writeValue(buffer, value) - cachedBytes = buffer.toByteArray() - return cachedBytes!! - } catch (e: Exception) { - throw OpenAIException("Error writing request", e) - } - } - - override fun writeTo(outputStream: OutputStream) { - outputStream.write(serialize()) - } - - override fun contentType(): String = "application/json" - - override fun contentLength(): Long { - return serialize().size.toLong() - } - - override fun repeatable(): Boolean = true - - override fun close() {} - } -} - -@JvmSynthetic -internal fun multipartFormData( - jsonMapper: JsonMapper, - parts: Array?>, -): HttpRequestBody { - val builder = MultipartEntityBuilder.create() - parts.forEach { part -> - if (part?.value != null) { - when (part.value) { - is JsonValue -> { - val buffer = ByteArrayOutputStream() - try { - jsonMapper.writeValue(buffer, part.value) - } catch (e: Exception) { - throw OpenAIException("Error serializing value to json", e) - } - builder.addBinaryBody( - part.name, - buffer.toByteArray(), - part.contentType, - part.filename, - ) - } - is Boolean -> - builder.addTextBody( - part.name, - if (part.value) "true" else "false", - part.contentType, - ) - is Int -> builder.addTextBody(part.name, part.value.toString(), part.contentType) - is Long -> builder.addTextBody(part.name, part.value.toString(), part.contentType) - is Double -> builder.addTextBody(part.name, part.value.toString(), part.contentType) - is ByteArray -> - builder.addBinaryBody(part.name, part.value, part.contentType, part.filename) - is String -> builder.addTextBody(part.name, part.value, part.contentType) - is Enum -> builder.addTextBody(part.name, part.value.toString(), part.contentType) - else -> - throw IllegalArgumentException( - "Unsupported content type: ${part.value::class.java.simpleName}" - ) - } - } - } - val entity = builder.build() - - return object : HttpRequestBody { - override fun writeTo(outputStream: OutputStream) { - try { - return entity.writeTo(outputStream) - } catch (e: Exception) { - throw OpenAIException("Error writing request", e) - } - } - - override fun contentType(): String = entity.contentType - - override fun contentLength(): Long = -1 - - override fun repeatable(): Boolean = entity.isRepeatable - - override fun close() = entity.close() - } -} diff --git a/openai-java-core/src/main/kotlin/com/openai/core/RequestOptions.kt b/openai-java-core/src/main/kotlin/com/openai/core/RequestOptions.kt index c72629c4e..1d97b7bba 100644 --- a/openai-java-core/src/main/kotlin/com/openai/core/RequestOptions.kt +++ b/openai-java-core/src/main/kotlin/com/openai/core/RequestOptions.kt @@ -2,13 +2,7 @@ package com.openai.core import java.time.Duration -class RequestOptions private constructor(val responseValidation: Boolean?, val timeout: Duration?) { - fun applyDefaults(options: RequestOptions): RequestOptions { - return RequestOptions( - responseValidation = this.responseValidation ?: options.responseValidation, - timeout = this.timeout ?: options.timeout, - ) - } +class RequestOptions private constructor(val responseValidation: Boolean?, val timeout: Timeout?) { companion object { @@ -16,22 +10,37 @@ class RequestOptions private constructor(val responseValidation: Boolean?, val t @JvmStatic fun none() = NONE + @JvmSynthetic + internal fun from(clientOptions: ClientOptions): RequestOptions = + builder() + .responseValidation(clientOptions.responseValidation) + .timeout(clientOptions.timeout) + .build() + @JvmStatic fun builder() = Builder() } + fun applyDefaults(options: RequestOptions): RequestOptions = + RequestOptions( + responseValidation = responseValidation ?: options.responseValidation, + timeout = + if (options.timeout != null && timeout != null) timeout.assign(options.timeout) + else timeout ?: options.timeout, + ) + class Builder internal constructor() { private var responseValidation: Boolean? = null - private var timeout: Duration? = null + private var timeout: Timeout? = null fun responseValidation(responseValidation: Boolean) = apply { this.responseValidation = responseValidation } - fun timeout(timeout: Duration) = apply { this.timeout = timeout } + fun timeout(timeout: Timeout) = apply { this.timeout = timeout } - fun build(): RequestOptions { - return RequestOptions(responseValidation, timeout) - } + fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build()) + + fun build(): RequestOptions = RequestOptions(responseValidation, timeout) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/core/Timeout.kt b/openai-java-core/src/main/kotlin/com/openai/core/Timeout.kt new file mode 100644 index 000000000..a27dd55a8 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/core/Timeout.kt @@ -0,0 +1,187 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.core + +import java.time.Duration +import java.util.Objects +import java.util.Optional + +/** A class containing timeouts for various processing phases of a request. */ +class Timeout +private constructor( + private val connect: Duration?, + private val read: Duration?, + private val write: Duration?, + private val request: Duration?, +) { + + /** + * The maximum time allowed to establish a connection with a host. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `Duration.ofMinutes(1)`. + */ + fun connect(): Duration = connect ?: Duration.ofMinutes(1) + + /** + * The maximum time allowed between two data packets when waiting for the server’s response. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `request()`. + */ + fun read(): Duration = read ?: request() + + /** + * The maximum time allowed between two data packets when sending the request to the server. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `request()`. + */ + fun write(): Duration = write ?: request() + + /** + * The maximum time allowed for a complete HTTP call, not including retries. + * + * This includes resolving DNS, connecting, writing the request body, server processing, as well + * as reading the response body. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `Duration.ofMinutes(10)`. + */ + fun request(): Duration = request ?: Duration.ofMinutes(10) + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun default() = builder().build() + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Timeout]. */ + class Builder internal constructor() { + + private var connect: Duration? = null + private var read: Duration? = null + private var write: Duration? = null + private var request: Duration? = null + + @JvmSynthetic + internal fun from(timeout: Timeout) = apply { + connect = timeout.connect + read = timeout.read + write = timeout.write + request = timeout.request + } + + /** + * The maximum time allowed to establish a connection with a host. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `Duration.ofMinutes(1)`. + */ + fun connect(connect: Duration?) = apply { this.connect = connect } + + /** + * The maximum time allowed to establish a connection with a host. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `Duration.ofMinutes(1)`. + */ + fun connect(connect: Optional) = connect(connect.orElse(null)) + + /** + * The maximum time allowed between two data packets when waiting for the server’s response. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `request()`. + */ + fun read(read: Duration?) = apply { this.read = read } + + /** + * The maximum time allowed between two data packets when waiting for the server’s response. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `request()`. + */ + fun read(read: Optional) = read(read.orElse(null)) + + /** + * The maximum time allowed between two data packets when sending the request to the server. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `request()`. + */ + fun write(write: Duration?) = apply { this.write = write } + + /** + * The maximum time allowed between two data packets when sending the request to the server. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `request()`. + */ + fun write(write: Optional) = write(write.orElse(null)) + + /** + * The maximum time allowed for a complete HTTP call, not including retries. + * + * This includes resolving DNS, connecting, writing the request body, server processing, as + * well as reading the response body. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `Duration.ofMinutes(10)`. + */ + fun request(request: Duration?) = apply { this.request = request } + + /** + * The maximum time allowed for a complete HTTP call, not including retries. + * + * This includes resolving DNS, connecting, writing the request body, server processing, as + * well as reading the response body. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `Duration.ofMinutes(10)`. + */ + fun request(request: Optional) = request(request.orElse(null)) + + fun build(): Timeout = Timeout(connect, read, write, request) + } + + @JvmSynthetic + internal fun assign(target: Timeout): Timeout = + target + .toBuilder() + .apply { + connect?.let(this::connect) + read?.let(this::read) + write?.let(this::write) + request?.let(this::request) + } + .build() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Timeout && connect == other.connect && read == other.read && write == other.write && request == other.request /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(connect, read, write, request) /* spotless:on */ + + override fun toString() = + "Timeout{connect=$connect, read=$read, write=$write, request=$request}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/core/Values.kt b/openai-java-core/src/main/kotlin/com/openai/core/Values.kt index 9d9ed6e86..87838aa1d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/core/Values.kt +++ b/openai-java-core/src/main/kotlin/com/openai/core/Values.kt @@ -27,10 +27,8 @@ import com.fasterxml.jackson.databind.node.JsonNodeType.POJO import com.fasterxml.jackson.databind.node.JsonNodeType.STRING import com.fasterxml.jackson.databind.ser.std.NullSerializer import com.openai.errors.OpenAIInvalidDataException -import java.nio.charset.Charset import java.util.Objects import java.util.Optional -import org.apache.hc.core5.http.ContentType @JsonDeserialize(using = JsonField.Deserializer::class) sealed class JsonField { @@ -287,12 +285,12 @@ private constructor( return true } - return other is KnownValue<*> && value == other.value + return other is KnownValue<*> && value contentEquals other.value } - override fun hashCode() = value.hashCode() + override fun hashCode() = contentHash(value) - override fun toString() = value.toString() + override fun toString() = value.contentToString() companion object { @JsonCreator @JvmStatic fun of(value: T) = KnownValue(value) @@ -462,15 +460,63 @@ annotation class ExcludeMissing ) annotation class NoAutoDetect -class MultipartFormValue -internal constructor( - val name: String, - val value: T, - val contentType: ContentType, - val filename: String? = null, +class MultipartField +private constructor( + @get:JvmName("value") val value: JsonField, + @get:JvmName("contentType") val contentType: String, + private val filename: String?, ) { - private val hashCode: Int by lazy { contentHash(name, value, contentType, filename) } + companion object { + + @JvmStatic fun of(value: T?) = builder().value(value).build() + + @JvmStatic fun of(value: JsonField) = builder().value(value).build() + + @JvmStatic fun builder() = Builder() + } + + fun filename(): Optional = Optional.ofNullable(filename) + + @JvmSynthetic + internal fun map(transform: (T) -> R): MultipartField = + MultipartField.builder() + .value(value.map(transform)) + .contentType(contentType) + .filename(filename) + .build() + + /** A builder for [MultipartField]. */ + class Builder internal constructor() { + + private var value: JsonField? = null + private var contentType: String? = null + private var filename: String? = null + + fun value(value: JsonField) = apply { this.value = value } + + fun value(value: T?) = value(JsonField.ofNullable(value)) + + fun contentType(contentType: String) = apply { this.contentType = contentType } + + fun filename(filename: String?) = apply { this.filename = filename } + + fun filename(filename: Optional) = filename(filename.orElse(null)) + + fun build(): MultipartField { + val value = checkRequired("value", value) + return MultipartField( + value, + contentType + ?: if (value is KnownValue && value.value is ByteArray) + "application/octet-stream" + else "text/plain; charset=utf-8", + filename, + ) + } + } + + private val hashCode: Int by lazy { contentHash(value, contentType, filename) } override fun hashCode(): Int = hashCode @@ -479,63 +525,12 @@ internal constructor( return true } - return other is MultipartFormValue<*> && - name == other.name && - value contentEquals other.value && + return other is MultipartField<*> && + value == other.value && contentType == other.contentType && filename == other.filename } override fun toString(): String = - "MultipartFormValue{name=$name, contentType=$contentType, filename=$filename, value=${valueToString()}}" - - private fun valueToString(): String = - when (value) { - is ByteArray -> "ByteArray of size ${value.size}" - else -> value.toString() - } - - companion object { - internal fun fromString( - name: String, - value: String, - contentType: ContentType, - ): MultipartFormValue = MultipartFormValue(name, value, contentType) - - internal fun fromBoolean( - name: String, - value: Boolean, - contentType: ContentType, - ): MultipartFormValue = MultipartFormValue(name, value, contentType) - - internal fun fromLong( - name: String, - value: Long, - contentType: ContentType, - ): MultipartFormValue = MultipartFormValue(name, value, contentType) - - internal fun fromDouble( - name: String, - value: Double, - contentType: ContentType, - ): MultipartFormValue = MultipartFormValue(name, value, contentType) - - internal fun fromEnum( - name: String, - value: T, - contentType: ContentType, - ): MultipartFormValue = MultipartFormValue(name, value, contentType) - - internal fun fromByteArray( - name: String, - value: ByteArray, - contentType: ContentType, - filename: String? = null, - ): MultipartFormValue = MultipartFormValue(name, value, contentType, filename) - } -} - -internal object ContentTypes { - val DefaultText = ContentType.create(ContentType.TEXT_PLAIN.mimeType, Charset.forName("UTF-8")) - val DefaultBinary = ContentType.DEFAULT_BINARY + "MultipartField{value=$value, contentType=$contentType, filename=$filename}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/core/http/HttpRequestBodies.kt b/openai-java-core/src/main/kotlin/com/openai/core/http/HttpRequestBodies.kt new file mode 100644 index 000000000..28c864f70 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/core/http/HttpRequestBodies.kt @@ -0,0 +1,90 @@ +// File generated from our OpenAPI spec by Stainless. + +@file:JvmName("HttpRequestBodies") + +package com.openai.core.http + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.json.JsonMapper +import com.fasterxml.jackson.databind.node.JsonNodeType +import com.openai.core.MultipartField +import com.openai.errors.OpenAIInvalidDataException +import java.io.OutputStream +import kotlin.jvm.optionals.getOrNull +import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder +import org.apache.hc.core5.http.ContentType +import org.apache.hc.core5.http.HttpEntity + +@JvmSynthetic +internal inline fun json(jsonMapper: JsonMapper, value: T): HttpRequestBody = + object : HttpRequestBody { + private val bytes: ByteArray by lazy { jsonMapper.writeValueAsBytes(value) } + + override fun writeTo(outputStream: OutputStream) = outputStream.write(bytes) + + override fun contentType(): String = "application/json" + + override fun contentLength(): Long = bytes.size.toLong() + + override fun repeatable(): Boolean = true + + override fun close() {} + } + +@JvmSynthetic +internal fun multipartFormData( + jsonMapper: JsonMapper, + fields: Map>, +): HttpRequestBody = + object : HttpRequestBody { + private val entity: HttpEntity by lazy { + MultipartEntityBuilder.create() + .apply { + fields.forEach { (name, field) -> + val node = jsonMapper.valueToTree(field.value) + serializePart(name, node).forEach { (name, bytes) -> + addBinaryBody( + name, + bytes, + ContentType.parseLenient(field.contentType), + field.filename().getOrNull(), + ) + } + } + } + .build() + } + + private fun serializePart(name: String, node: JsonNode): Sequence> = + when (node.nodeType) { + JsonNodeType.MISSING, + JsonNodeType.NULL -> emptySequence() + JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue()) + JsonNodeType.STRING -> sequenceOf(name to node.textValue().toByteArray()) + JsonNodeType.BOOLEAN -> + sequenceOf(name to node.booleanValue().toString().toByteArray()) + JsonNodeType.NUMBER -> + sequenceOf(name to node.numberValue().toString().toByteArray()) + JsonNodeType.ARRAY -> + node.elements().asSequence().flatMap { element -> + serializePart("$name[]", element) + } + JsonNodeType.OBJECT -> + node.fields().asSequence().flatMap { (key, value) -> + serializePart("$name[$key]", value) + } + JsonNodeType.POJO, + null -> + throw OpenAIInvalidDataException("Unexpected JsonNode type: ${node.nodeType}") + } + + override fun writeTo(outputStream: OutputStream) = entity.writeTo(outputStream) + + override fun contentType(): String = entity.contentType + + override fun contentLength(): Long = entity.contentLength + + override fun repeatable(): Boolean = entity.isRepeatable + + override fun close() = entity.close() + } diff --git a/openai-java-core/src/main/kotlin/com/openai/core/http/HttpResponseFor.kt b/openai-java-core/src/main/kotlin/com/openai/core/http/HttpResponseFor.kt new file mode 100644 index 000000000..eaf211a05 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/core/http/HttpResponseFor.kt @@ -0,0 +1,23 @@ +package com.openai.core.http + +import java.io.InputStream + +interface HttpResponseFor : HttpResponse { + + fun parse(): T +} + +@JvmSynthetic +internal fun HttpResponse.parseable(parse: () -> T): HttpResponseFor = + object : HttpResponseFor { + + override fun parse(): T = parse() + + override fun statusCode(): Int = this@parseable.statusCode() + + override fun headers(): Headers = this@parseable.headers() + + override fun body(): InputStream = this@parseable.body() + + override fun close() = this@parseable.close() + } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/Assistant.kt b/openai-java-core/src/main/kotlin/com/openai/models/Assistant.kt index 7b80a02e1..ce1e4c05c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/Assistant.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/Assistant.kt @@ -11,6 +11,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -432,15 +433,7 @@ private constructor( */ fun addTool(tool: AssistantTool) = apply { tools = - (tools ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(tool) - } + (tools ?: JsonField.of(mutableListOf())).also { checkKnown("tools", it).add(tool) } } /** @@ -936,14 +929,8 @@ private constructor( */ fun addFileId(fileId: String) = apply { fileIds = - (fileIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(fileId) + (fileIds ?: JsonField.of(mutableListOf())).also { + checkKnown("fileIds", it).add(fileId) } } @@ -1085,14 +1072,8 @@ private constructor( */ fun addVectorStoreId(vectorStoreId: String) = apply { vectorStoreIds = - (vectorStoreIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(vectorStoreId) + (vectorStoreIds ?: JsonField.of(mutableListOf())).also { + checkKnown("vectorStoreIds", it).add(vectorStoreId) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/AudioModel.kt b/openai-java-core/src/main/kotlin/com/openai/models/AudioModel.kt new file mode 100644 index 000000000..80e8e9f09 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/AudioModel.kt @@ -0,0 +1,98 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonCreator +import com.openai.core.Enum +import com.openai.core.JsonField +import com.openai.errors.OpenAIInvalidDataException + +class AudioModel @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't match + * any known member, and you want to know that value. For example, if the SDK is on an older + * version than the API, then the API may respond with new members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val WHISPER_1 = of("whisper-1") + + @JvmStatic fun of(value: String) = AudioModel(JsonField.of(value)) + } + + /** An enum containing [AudioModel]'s known values. */ + enum class Known { + WHISPER_1 + } + + /** + * An enum containing [AudioModel]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AudioModel] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the SDK + * is on an older version than the API, then the API may respond with new members that the SDK + * is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + WHISPER_1, + /** An enum member indicating that [AudioModel] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] if + * the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want to + * throw for the unknown case. + */ + fun value(): Value = + when (this) { + WHISPER_1 -> Value.WHISPER_1 + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't want + * to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + WHISPER_1 -> Known.WHISPER_1 + else -> throw OpenAIInvalidDataException("Unknown AudioModel: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging and + * generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the expected + * primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is AudioModel && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/AudioResponseFormat.kt b/openai-java-core/src/main/kotlin/com/openai/models/AudioResponseFormat.kt new file mode 100644 index 000000000..e75d48551 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/AudioResponseFormat.kt @@ -0,0 +1,130 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonCreator +import com.openai.core.Enum +import com.openai.core.JsonField +import com.openai.errors.OpenAIInvalidDataException + +/** + * The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, or + * `vtt`. + */ +class AudioResponseFormat @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't match + * any known member, and you want to know that value. For example, if the SDK is on an older + * version than the API, then the API may respond with new members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val JSON = of("json") + + @JvmField val TEXT = of("text") + + @JvmField val SRT = of("srt") + + @JvmField val VERBOSE_JSON = of("verbose_json") + + @JvmField val VTT = of("vtt") + + @JvmStatic fun of(value: String) = AudioResponseFormat(JsonField.of(value)) + } + + /** An enum containing [AudioResponseFormat]'s known values. */ + enum class Known { + JSON, + TEXT, + SRT, + VERBOSE_JSON, + VTT, + } + + /** + * An enum containing [AudioResponseFormat]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AudioResponseFormat] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the SDK + * is on an older version than the API, then the API may respond with new members that the SDK + * is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + JSON, + TEXT, + SRT, + VERBOSE_JSON, + VTT, + /** + * An enum member indicating that [AudioResponseFormat] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] if + * the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want to + * throw for the unknown case. + */ + fun value(): Value = + when (this) { + JSON -> Value.JSON + TEXT -> Value.TEXT + SRT -> Value.SRT + VERBOSE_JSON -> Value.VERBOSE_JSON + VTT -> Value.VTT + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't want + * to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + JSON -> Known.JSON + TEXT -> Known.TEXT + SRT -> Known.SRT + VERBOSE_JSON -> Known.VERBOSE_JSON + VTT -> Known.VTT + else -> throw OpenAIInvalidDataException("Unknown AudioResponseFormat: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging and + * generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the expected + * primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is AudioResponseFormat && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/AudioSpeechCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/AudioSpeechCreateParams.kt new file mode 100644 index 000000000..24b65ffa6 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/AudioSpeechCreateParams.kt @@ -0,0 +1,846 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.openai.core.Enum +import com.openai.core.ExcludeMissing +import com.openai.core.JsonField +import com.openai.core.JsonMissing +import com.openai.core.JsonValue +import com.openai.core.NoAutoDetect +import com.openai.core.Params +import com.openai.core.checkRequired +import com.openai.core.http.Headers +import com.openai.core.http.QueryParams +import com.openai.core.immutableEmptyMap +import com.openai.core.toImmutable +import com.openai.errors.OpenAIInvalidDataException +import java.util.Objects +import java.util.Optional + +/** Generates audio from the input text. */ +class AudioSpeechCreateParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** The text to generate audio for. The maximum length is 4096 characters. */ + fun input(): String = body.input() + + /** + * One of the available [TTS models](https://platform.openai.com/docs/models#tts): `tts-1` or + * `tts-1-hd` + */ + fun model(): SpeechModel = body.model() + + /** + * The voice to use when generating the audio. Supported voices are `alloy`, `ash`, `coral`, + * `echo`, `fable`, `onyx`, `nova`, `sage` and `shimmer`. Previews of the voices are available + * in the + * [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). + */ + fun voice(): Voice = body.voice() + + /** + * The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`. + */ + fun responseFormat(): Optional = body.responseFormat() + + /** + * The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the default. + */ + fun speed(): Optional = body.speed() + + /** The text to generate audio for. The maximum length is 4096 characters. */ + fun _input(): JsonField = body._input() + + /** + * One of the available [TTS models](https://platform.openai.com/docs/models#tts): `tts-1` or + * `tts-1-hd` + */ + fun _model(): JsonField = body._model() + + /** + * The voice to use when generating the audio. Supported voices are `alloy`, `ash`, `coral`, + * `echo`, `fable`, `onyx`, `nova`, `sage` and `shimmer`. Previews of the voices are available + * in the + * [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). + */ + fun _voice(): JsonField = body._voice() + + /** + * The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`. + */ + fun _responseFormat(): JsonField = body._responseFormat() + + /** + * The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the default. + */ + fun _speed(): JsonField = body._speed() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + @JvmSynthetic internal fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + @NoAutoDetect + class Body + @JsonCreator + private constructor( + @JsonProperty("input") + @ExcludeMissing + private val input: JsonField = JsonMissing.of(), + @JsonProperty("model") + @ExcludeMissing + private val model: JsonField = JsonMissing.of(), + @JsonProperty("voice") + @ExcludeMissing + private val voice: JsonField = JsonMissing.of(), + @JsonProperty("response_format") + @ExcludeMissing + private val responseFormat: JsonField = JsonMissing.of(), + @JsonProperty("speed") + @ExcludeMissing + private val speed: JsonField = JsonMissing.of(), + @JsonAnySetter + private val additionalProperties: Map = immutableEmptyMap(), + ) { + + /** The text to generate audio for. The maximum length is 4096 characters. */ + fun input(): String = input.getRequired("input") + + /** + * One of the available [TTS models](https://platform.openai.com/docs/models#tts): `tts-1` + * or `tts-1-hd` + */ + fun model(): SpeechModel = model.getRequired("model") + + /** + * The voice to use when generating the audio. Supported voices are `alloy`, `ash`, `coral`, + * `echo`, `fable`, `onyx`, `nova`, `sage` and `shimmer`. Previews of the voices are + * available in the + * [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). + */ + fun voice(): Voice = voice.getRequired("voice") + + /** + * The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and + * `pcm`. + */ + fun responseFormat(): Optional = + Optional.ofNullable(responseFormat.getNullable("response_format")) + + /** + * The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the + * default. + */ + fun speed(): Optional = Optional.ofNullable(speed.getNullable("speed")) + + /** The text to generate audio for. The maximum length is 4096 characters. */ + @JsonProperty("input") @ExcludeMissing fun _input(): JsonField = input + + /** + * One of the available [TTS models](https://platform.openai.com/docs/models#tts): `tts-1` + * or `tts-1-hd` + */ + @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + + /** + * The voice to use when generating the audio. Supported voices are `alloy`, `ash`, `coral`, + * `echo`, `fable`, `onyx`, `nova`, `sage` and `shimmer`. Previews of the voices are + * available in the + * [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). + */ + @JsonProperty("voice") @ExcludeMissing fun _voice(): JsonField = voice + + /** + * The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and + * `pcm`. + */ + @JsonProperty("response_format") + @ExcludeMissing + fun _responseFormat(): JsonField = responseFormat + + /** + * The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the + * default. + */ + @JsonProperty("speed") @ExcludeMissing fun _speed(): JsonField = speed + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + input() + model() + voice() + responseFormat() + speed() + validated = true + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var input: JsonField? = null + private var model: JsonField? = null + private var voice: JsonField? = null + private var responseFormat: JsonField = JsonMissing.of() + private var speed: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + input = body.input + model = body.model + voice = body.voice + responseFormat = body.responseFormat + speed = body.speed + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** The text to generate audio for. The maximum length is 4096 characters. */ + fun input(input: String) = input(JsonField.of(input)) + + /** The text to generate audio for. The maximum length is 4096 characters. */ + fun input(input: JsonField) = apply { this.input = input } + + /** + * One of the available [TTS models](https://platform.openai.com/docs/models#tts): + * `tts-1` or `tts-1-hd` + */ + fun model(model: SpeechModel) = model(JsonField.of(model)) + + /** + * One of the available [TTS models](https://platform.openai.com/docs/models#tts): + * `tts-1` or `tts-1-hd` + */ + fun model(model: JsonField) = apply { this.model = model } + + /** + * One of the available [TTS models](https://platform.openai.com/docs/models#tts): + * `tts-1` or `tts-1-hd` + */ + fun model(value: String) = model(SpeechModel.of(value)) + + /** + * The voice to use when generating the audio. Supported voices are `alloy`, `ash`, + * `coral`, `echo`, `fable`, `onyx`, `nova`, `sage` and `shimmer`. Previews of the + * voices are available in the + * [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). + */ + fun voice(voice: Voice) = voice(JsonField.of(voice)) + + /** + * The voice to use when generating the audio. Supported voices are `alloy`, `ash`, + * `coral`, `echo`, `fable`, `onyx`, `nova`, `sage` and `shimmer`. Previews of the + * voices are available in the + * [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). + */ + fun voice(voice: JsonField) = apply { this.voice = voice } + + /** + * The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, + * and `pcm`. + */ + fun responseFormat(responseFormat: ResponseFormat) = + responseFormat(JsonField.of(responseFormat)) + + /** + * The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, + * and `pcm`. + */ + fun responseFormat(responseFormat: JsonField) = apply { + this.responseFormat = responseFormat + } + + /** + * The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the + * default. + */ + fun speed(speed: Double) = speed(JsonField.of(speed)) + + /** + * The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the + * default. + */ + fun speed(speed: JsonField) = apply { this.speed = speed } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + fun build(): Body = + Body( + checkRequired("input", input), + checkRequired("model", model), + checkRequired("voice", voice), + responseFormat, + speed, + additionalProperties.toImmutable(), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && input == other.input && model == other.model && voice == other.voice && responseFormat == other.responseFormat && speed == other.speed && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(input, model, voice, responseFormat, speed, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{input=$input, model=$model, voice=$voice, responseFormat=$responseFormat, speed=$speed, additionalProperties=$additionalProperties}" + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AudioSpeechCreateParams]. */ + @NoAutoDetect + class Builder internal constructor() { + + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(audioSpeechCreateParams: AudioSpeechCreateParams) = apply { + body = audioSpeechCreateParams.body.toBuilder() + additionalHeaders = audioSpeechCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = audioSpeechCreateParams.additionalQueryParams.toBuilder() + } + + /** The text to generate audio for. The maximum length is 4096 characters. */ + fun input(input: String) = apply { body.input(input) } + + /** The text to generate audio for. The maximum length is 4096 characters. */ + fun input(input: JsonField) = apply { body.input(input) } + + /** + * One of the available [TTS models](https://platform.openai.com/docs/models#tts): `tts-1` + * or `tts-1-hd` + */ + fun model(model: SpeechModel) = apply { body.model(model) } + + /** + * One of the available [TTS models](https://platform.openai.com/docs/models#tts): `tts-1` + * or `tts-1-hd` + */ + fun model(model: JsonField) = apply { body.model(model) } + + /** + * One of the available [TTS models](https://platform.openai.com/docs/models#tts): `tts-1` + * or `tts-1-hd` + */ + fun model(value: String) = apply { body.model(value) } + + /** + * The voice to use when generating the audio. Supported voices are `alloy`, `ash`, `coral`, + * `echo`, `fable`, `onyx`, `nova`, `sage` and `shimmer`. Previews of the voices are + * available in the + * [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). + */ + fun voice(voice: Voice) = apply { body.voice(voice) } + + /** + * The voice to use when generating the audio. Supported voices are `alloy`, `ash`, `coral`, + * `echo`, `fable`, `onyx`, `nova`, `sage` and `shimmer`. Previews of the voices are + * available in the + * [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). + */ + fun voice(voice: JsonField) = apply { body.voice(voice) } + + /** + * The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and + * `pcm`. + */ + fun responseFormat(responseFormat: ResponseFormat) = apply { + body.responseFormat(responseFormat) + } + + /** + * The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and + * `pcm`. + */ + fun responseFormat(responseFormat: JsonField) = apply { + body.responseFormat(responseFormat) + } + + /** + * The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the + * default. + */ + fun speed(speed: Double) = apply { body.speed(speed) } + + /** + * The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the + * default. + */ + fun speed(speed: JsonField) = apply { body.speed(speed) } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + fun build(): AudioSpeechCreateParams = + AudioSpeechCreateParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + /** + * The voice to use when generating the audio. Supported voices are `alloy`, `ash`, `coral`, + * `echo`, `fable`, `onyx`, `nova`, `sage` and `shimmer`. Previews of the voices are available + * in the + * [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). + */ + class Voice @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val ALLOY = of("alloy") + + @JvmField val ASH = of("ash") + + @JvmField val CORAL = of("coral") + + @JvmField val ECHO = of("echo") + + @JvmField val FABLE = of("fable") + + @JvmField val ONYX = of("onyx") + + @JvmField val NOVA = of("nova") + + @JvmField val SAGE = of("sage") + + @JvmField val SHIMMER = of("shimmer") + + @JvmStatic fun of(value: String) = Voice(JsonField.of(value)) + } + + /** An enum containing [Voice]'s known values. */ + enum class Known { + ALLOY, + ASH, + CORAL, + ECHO, + FABLE, + ONYX, + NOVA, + SAGE, + SHIMMER, + } + + /** + * An enum containing [Voice]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Voice] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + ALLOY, + ASH, + CORAL, + ECHO, + FABLE, + ONYX, + NOVA, + SAGE, + SHIMMER, + /** An enum member indicating that [Voice] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + ALLOY -> Value.ALLOY + ASH -> Value.ASH + CORAL -> Value.CORAL + ECHO -> Value.ECHO + FABLE -> Value.FABLE + ONYX -> Value.ONYX + NOVA -> Value.NOVA + SAGE -> Value.SAGE + SHIMMER -> Value.SHIMMER + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + ALLOY -> Known.ALLOY + ASH -> Known.ASH + CORAL -> Known.CORAL + ECHO -> Known.ECHO + FABLE -> Known.FABLE + ONYX -> Known.ONYX + NOVA -> Known.NOVA + SAGE -> Known.SAGE + SHIMMER -> Known.SHIMMER + else -> throw OpenAIInvalidDataException("Unknown Voice: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Voice && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** + * The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`. + */ + class ResponseFormat @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val MP3 = of("mp3") + + @JvmField val OPUS = of("opus") + + @JvmField val AAC = of("aac") + + @JvmField val FLAC = of("flac") + + @JvmField val WAV = of("wav") + + @JvmField val PCM = of("pcm") + + @JvmStatic fun of(value: String) = ResponseFormat(JsonField.of(value)) + } + + /** An enum containing [ResponseFormat]'s known values. */ + enum class Known { + MP3, + OPUS, + AAC, + FLAC, + WAV, + PCM, + } + + /** + * An enum containing [ResponseFormat]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [ResponseFormat] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + MP3, + OPUS, + AAC, + FLAC, + WAV, + PCM, + /** + * An enum member indicating that [ResponseFormat] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + MP3 -> Value.MP3 + OPUS -> Value.OPUS + AAC -> Value.AAC + FLAC -> Value.FLAC + WAV -> Value.WAV + PCM -> Value.PCM + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + MP3 -> Known.MP3 + OPUS -> Known.OPUS + AAC -> Known.AAC + FLAC -> Known.FLAC + WAV -> Known.WAV + PCM -> Known.PCM + else -> throw OpenAIInvalidDataException("Unknown ResponseFormat: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is ResponseFormat && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is AudioSpeechCreateParams && body == other.body && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(body, additionalHeaders, additionalQueryParams) /* spotless:on */ + + override fun toString() = + "AudioSpeechCreateParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/AudioTranscriptionCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/AudioTranscriptionCreateParams.kt new file mode 100644 index 000000000..48a79d021 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/AudioTranscriptionCreateParams.kt @@ -0,0 +1,836 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonCreator +import com.openai.core.Enum +import com.openai.core.JsonField +import com.openai.core.MultipartField +import com.openai.core.NoAutoDetect +import com.openai.core.Params +import com.openai.core.checkKnown +import com.openai.core.checkRequired +import com.openai.core.http.Headers +import com.openai.core.http.QueryParams +import com.openai.core.toImmutable +import com.openai.errors.OpenAIInvalidDataException +import java.util.Objects +import java.util.Optional + +/** Transcribes audio into the input language. */ +class AudioTranscriptionCreateParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, + * mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun file(): ByteArray = body.file() + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 + * model) is currently available. + */ + fun model(): AudioModel = body.model() + + /** + * The language of the input audio. Supplying the input language in + * [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format will + * improve accuracy and latency. + */ + fun language(): Optional = body.language() + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should match the + * audio language. + */ + fun prompt(): Optional = body.prompt() + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, or + * `vtt`. + */ + fun responseFormat(): Optional = body.responseFormat() + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more + * random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, + * the model will use [log probability](https://en.wikipedia.org/wiki/Log_probability) to + * automatically increase the temperature until certain thresholds are hit. + */ + fun temperature(): Optional = body.temperature() + + /** + * The timestamp granularities to populate for this transcription. `response_format` must be set + * `verbose_json` to use timestamp granularities. Either or both of these options are supported: + * `word`, or `segment`. Note: There is no additional latency for segment timestamps, but + * generating word timestamps incurs additional latency. + */ + fun timestampGranularities(): Optional> = + body.timestampGranularities() + + /** + * The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, + * mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun _file(): MultipartField = body._file() + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 + * model) is currently available. + */ + fun _model(): MultipartField = body._model() + + /** + * The language of the input audio. Supplying the input language in + * [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format will + * improve accuracy and latency. + */ + fun _language(): MultipartField = body._language() + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should match the + * audio language. + */ + fun _prompt(): MultipartField = body._prompt() + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, or + * `vtt`. + */ + fun _responseFormat(): MultipartField = body._responseFormat() + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more + * random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, + * the model will use [log probability](https://en.wikipedia.org/wiki/Log_probability) to + * automatically increase the temperature until certain thresholds are hit. + */ + fun _temperature(): MultipartField = body._temperature() + + /** + * The timestamp granularities to populate for this transcription. `response_format` must be set + * `verbose_json` to use timestamp granularities. Either or both of these options are supported: + * `word`, or `segment`. Note: There is no additional latency for segment timestamps, but + * generating word timestamps incurs additional latency. + */ + fun _timestampGranularities(): MultipartField> = + body._timestampGranularities() + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + @JvmSynthetic + internal fun _body(): Map> = + mapOf( + "file" to _file(), + "model" to _model(), + "language" to _language(), + "prompt" to _prompt(), + "response_format" to _responseFormat(), + "temperature" to _temperature(), + "timestamp_granularities" to _timestampGranularities(), + ) + .toImmutable() + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + @NoAutoDetect + class Body + @JsonCreator + private constructor( + private val file: MultipartField, + private val model: MultipartField, + private val language: MultipartField, + private val prompt: MultipartField, + private val responseFormat: MultipartField, + private val temperature: MultipartField, + private val timestampGranularities: MultipartField>, + ) { + + /** + * The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, + * mp4, mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun file(): ByteArray = file.value.getRequired("file") + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 + * model) is currently available. + */ + fun model(): AudioModel = model.value.getRequired("model") + + /** + * The language of the input audio. Supplying the input language in + * [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format + * will improve accuracy and latency. + */ + fun language(): Optional = + Optional.ofNullable(language.value.getNullable("language")) + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should match + * the audio language. + */ + fun prompt(): Optional = Optional.ofNullable(prompt.value.getNullable("prompt")) + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, + * or `vtt`. + */ + fun responseFormat(): Optional = + Optional.ofNullable(responseFormat.value.getNullable("response_format")) + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output + * more random, while lower values like 0.2 will make it more focused and deterministic. If + * set to 0, the model will use + * [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically + * increase the temperature until certain thresholds are hit. + */ + fun temperature(): Optional = + Optional.ofNullable(temperature.value.getNullable("temperature")) + + /** + * The timestamp granularities to populate for this transcription. `response_format` must be + * set `verbose_json` to use timestamp granularities. Either or both of these options are + * supported: `word`, or `segment`. Note: There is no additional latency for segment + * timestamps, but generating word timestamps incurs additional latency. + */ + fun timestampGranularities(): Optional> = + Optional.ofNullable(timestampGranularities.value.getNullable("timestamp_granularities")) + + /** + * The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, + * mp4, mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun _file(): MultipartField = file + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 + * model) is currently available. + */ + fun _model(): MultipartField = model + + /** + * The language of the input audio. Supplying the input language in + * [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format + * will improve accuracy and latency. + */ + fun _language(): MultipartField = language + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should match + * the audio language. + */ + fun _prompt(): MultipartField = prompt + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, + * or `vtt`. + */ + fun _responseFormat(): MultipartField = responseFormat + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output + * more random, while lower values like 0.2 will make it more focused and deterministic. If + * set to 0, the model will use + * [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically + * increase the temperature until certain thresholds are hit. + */ + fun _temperature(): MultipartField = temperature + + /** + * The timestamp granularities to populate for this transcription. `response_format` must be + * set `verbose_json` to use timestamp granularities. Either or both of these options are + * supported: `word`, or `segment`. Note: There is no additional latency for segment + * timestamps, but generating word timestamps incurs additional latency. + */ + fun _timestampGranularities(): MultipartField> = + timestampGranularities + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + file() + model() + language() + prompt() + responseFormat() + temperature() + timestampGranularities() + validated = true + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var file: MultipartField? = null + private var model: MultipartField? = null + private var language: MultipartField = MultipartField.of(null) + private var prompt: MultipartField = MultipartField.of(null) + private var responseFormat: MultipartField = + MultipartField.of(null) + private var temperature: MultipartField = MultipartField.of(null) + private var timestampGranularities: MultipartField>? = + null + + @JvmSynthetic + internal fun from(body: Body) = apply { + file = body.file + model = body.model + language = body.language + prompt = body.prompt + responseFormat = body.responseFormat + temperature = body.temperature + timestampGranularities = body.timestampGranularities.map { it.toMutableList() } + } + + /** + * The audio file object (not file name) to transcribe, in one of these formats: flac, + * mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun file(file: ByteArray) = file(MultipartField.of(file)) + + /** + * The audio file object (not file name) to transcribe, in one of these formats: flac, + * mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun file(file: MultipartField) = apply { this.file = file } + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper + * V2 model) is currently available. + */ + fun model(model: AudioModel) = model(MultipartField.of(model)) + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper + * V2 model) is currently available. + */ + fun model(model: MultipartField) = apply { this.model = model } + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper + * V2 model) is currently available. + */ + fun model(value: String) = model(AudioModel.of(value)) + + /** + * The language of the input audio. Supplying the input language in + * [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format + * will improve accuracy and latency. + */ + fun language(language: String) = language(MultipartField.of(language)) + + /** + * The language of the input audio. Supplying the input language in + * [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format + * will improve accuracy and latency. + */ + fun language(language: MultipartField) = apply { this.language = language } + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should + * match the audio language. + */ + fun prompt(prompt: String) = prompt(MultipartField.of(prompt)) + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should + * match the audio language. + */ + fun prompt(prompt: MultipartField) = apply { this.prompt = prompt } + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, + * `verbose_json`, or `vtt`. + */ + fun responseFormat(responseFormat: AudioResponseFormat) = + responseFormat(MultipartField.of(responseFormat)) + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, + * `verbose_json`, or `vtt`. + */ + fun responseFormat(responseFormat: MultipartField) = apply { + this.responseFormat = responseFormat + } + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + * output more random, while lower values like 0.2 will make it more focused and + * deterministic. If set to 0, the model will use + * [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically + * increase the temperature until certain thresholds are hit. + */ + fun temperature(temperature: Double) = temperature(MultipartField.of(temperature)) + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + * output more random, while lower values like 0.2 will make it more focused and + * deterministic. If set to 0, the model will use + * [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically + * increase the temperature until certain thresholds are hit. + */ + fun temperature(temperature: MultipartField) = apply { + this.temperature = temperature + } + + /** + * The timestamp granularities to populate for this transcription. `response_format` + * must be set `verbose_json` to use timestamp granularities. Either or both of these + * options are supported: `word`, or `segment`. Note: There is no additional latency for + * segment timestamps, but generating word timestamps incurs additional latency. + */ + fun timestampGranularities(timestampGranularities: List) = + timestampGranularities(MultipartField.of(timestampGranularities)) + + /** + * The timestamp granularities to populate for this transcription. `response_format` + * must be set `verbose_json` to use timestamp granularities. Either or both of these + * options are supported: `word`, or `segment`. Note: There is no additional latency for + * segment timestamps, but generating word timestamps incurs additional latency. + */ + fun timestampGranularities( + timestampGranularities: MultipartField> + ) = apply { + this.timestampGranularities = timestampGranularities.map { it.toMutableList() } + } + + /** + * The timestamp granularities to populate for this transcription. `response_format` + * must be set `verbose_json` to use timestamp granularities. Either or both of these + * options are supported: `word`, or `segment`. Note: There is no additional latency for + * segment timestamps, but generating word timestamps incurs additional latency. + */ + fun addTimestampGranularity(timestampGranularity: TimestampGranularity) = apply { + timestampGranularities = + (timestampGranularities ?: MultipartField.of(mutableListOf())).also { + checkKnown("timestampGranularities", it).add(timestampGranularity) + } + } + + fun build(): Body = + Body( + checkRequired("file", file), + checkRequired("model", model), + language, + prompt, + responseFormat, + temperature, + (timestampGranularities ?: MultipartField.of(null)).map { it.toImmutable() }, + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && file == other.file && model == other.model && language == other.language && prompt == other.prompt && responseFormat == other.responseFormat && temperature == other.temperature && timestampGranularities == other.timestampGranularities /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(file, model, language, prompt, responseFormat, temperature, timestampGranularities) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{file=$file, model=$model, language=$language, prompt=$prompt, responseFormat=$responseFormat, temperature=$temperature, timestampGranularities=$timestampGranularities}" + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AudioTranscriptionCreateParams]. */ + @NoAutoDetect + class Builder internal constructor() { + + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(audioTranscriptionCreateParams: AudioTranscriptionCreateParams) = apply { + body = audioTranscriptionCreateParams.body.toBuilder() + additionalHeaders = audioTranscriptionCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = audioTranscriptionCreateParams.additionalQueryParams.toBuilder() + } + + /** + * The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, + * mp4, mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun file(file: ByteArray) = apply { body.file(file) } + + /** + * The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, + * mp4, mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun file(file: MultipartField) = apply { body.file(file) } + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 + * model) is currently available. + */ + fun model(model: AudioModel) = apply { body.model(model) } + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 + * model) is currently available. + */ + fun model(model: MultipartField) = apply { body.model(model) } + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 + * model) is currently available. + */ + fun model(value: String) = apply { body.model(value) } + + /** + * The language of the input audio. Supplying the input language in + * [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format + * will improve accuracy and latency. + */ + fun language(language: String) = apply { body.language(language) } + + /** + * The language of the input audio. Supplying the input language in + * [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format + * will improve accuracy and latency. + */ + fun language(language: MultipartField) = apply { body.language(language) } + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should match + * the audio language. + */ + fun prompt(prompt: String) = apply { body.prompt(prompt) } + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should match + * the audio language. + */ + fun prompt(prompt: MultipartField) = apply { body.prompt(prompt) } + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, + * or `vtt`. + */ + fun responseFormat(responseFormat: AudioResponseFormat) = apply { + body.responseFormat(responseFormat) + } + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, + * or `vtt`. + */ + fun responseFormat(responseFormat: MultipartField) = apply { + body.responseFormat(responseFormat) + } + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output + * more random, while lower values like 0.2 will make it more focused and deterministic. If + * set to 0, the model will use + * [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically + * increase the temperature until certain thresholds are hit. + */ + fun temperature(temperature: Double) = apply { body.temperature(temperature) } + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output + * more random, while lower values like 0.2 will make it more focused and deterministic. If + * set to 0, the model will use + * [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically + * increase the temperature until certain thresholds are hit. + */ + fun temperature(temperature: MultipartField) = apply { + body.temperature(temperature) + } + + /** + * The timestamp granularities to populate for this transcription. `response_format` must be + * set `verbose_json` to use timestamp granularities. Either or both of these options are + * supported: `word`, or `segment`. Note: There is no additional latency for segment + * timestamps, but generating word timestamps incurs additional latency. + */ + fun timestampGranularities(timestampGranularities: List) = apply { + body.timestampGranularities(timestampGranularities) + } + + /** + * The timestamp granularities to populate for this transcription. `response_format` must be + * set `verbose_json` to use timestamp granularities. Either or both of these options are + * supported: `word`, or `segment`. Note: There is no additional latency for segment + * timestamps, but generating word timestamps incurs additional latency. + */ + fun timestampGranularities( + timestampGranularities: MultipartField> + ) = apply { body.timestampGranularities(timestampGranularities) } + + /** + * The timestamp granularities to populate for this transcription. `response_format` must be + * set `verbose_json` to use timestamp granularities. Either or both of these options are + * supported: `word`, or `segment`. Note: There is no additional latency for segment + * timestamps, but generating word timestamps incurs additional latency. + */ + fun addTimestampGranularity(timestampGranularity: TimestampGranularity) = apply { + body.addTimestampGranularity(timestampGranularity) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + fun build(): AudioTranscriptionCreateParams = + AudioTranscriptionCreateParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + class TimestampGranularity + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val WORD = of("word") + + @JvmField val SEGMENT = of("segment") + + @JvmStatic fun of(value: String) = TimestampGranularity(JsonField.of(value)) + } + + /** An enum containing [TimestampGranularity]'s known values. */ + enum class Known { + WORD, + SEGMENT, + } + + /** + * An enum containing [TimestampGranularity]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [TimestampGranularity] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + WORD, + SEGMENT, + /** + * An enum member indicating that [TimestampGranularity] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + WORD -> Value.WORD + SEGMENT -> Value.SEGMENT + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + WORD -> Known.WORD + SEGMENT -> Known.SEGMENT + else -> throw OpenAIInvalidDataException("Unknown TimestampGranularity: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is TimestampGranularity && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is AudioTranscriptionCreateParams && body == other.body && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(body, additionalHeaders, additionalQueryParams) /* spotless:on */ + + override fun toString() = + "AudioTranscriptionCreateParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/AudioTranscriptionCreateResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/AudioTranscriptionCreateResponse.kt new file mode 100644 index 000000000..60099d3e5 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/AudioTranscriptionCreateResponse.kt @@ -0,0 +1,189 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.BaseDeserializer +import com.openai.core.BaseSerializer +import com.openai.core.JsonValue +import com.openai.core.getOrThrow +import com.openai.errors.OpenAIInvalidDataException +import java.util.Objects +import java.util.Optional + +/** Represents a transcription response returned by model, based on the provided input. */ +@JsonDeserialize(using = AudioTranscriptionCreateResponse.Deserializer::class) +@JsonSerialize(using = AudioTranscriptionCreateResponse.Serializer::class) +class AudioTranscriptionCreateResponse +private constructor( + private val transcription: Transcription? = null, + private val transcriptionVerbose: TranscriptionVerbose? = null, + private val _json: JsonValue? = null, +) { + + /** Represents a transcription response returned by model, based on the provided input. */ + fun transcription(): Optional = Optional.ofNullable(transcription) + + /** + * Represents a verbose json transcription response returned by model, based on the provided + * input. + */ + fun transcriptionVerbose(): Optional = + Optional.ofNullable(transcriptionVerbose) + + fun isTranscription(): Boolean = transcription != null + + fun isTranscriptionVerbose(): Boolean = transcriptionVerbose != null + + /** Represents a transcription response returned by model, based on the provided input. */ + fun asTranscription(): Transcription = transcription.getOrThrow("transcription") + + /** + * Represents a verbose json transcription response returned by model, based on the provided + * input. + */ + fun asTranscriptionVerbose(): TranscriptionVerbose = + transcriptionVerbose.getOrThrow("transcriptionVerbose") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T { + return when { + transcription != null -> visitor.visitTranscription(transcription) + transcriptionVerbose != null -> visitor.visitTranscriptionVerbose(transcriptionVerbose) + else -> visitor.unknown(_json) + } + } + + private var validated: Boolean = false + + fun validate(): AudioTranscriptionCreateResponse = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitTranscription(transcription: Transcription) { + transcription.validate() + } + + override fun visitTranscriptionVerbose(transcriptionVerbose: TranscriptionVerbose) { + transcriptionVerbose.validate() + } + } + ) + validated = true + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is AudioTranscriptionCreateResponse && transcription == other.transcription && transcriptionVerbose == other.transcriptionVerbose /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(transcription, transcriptionVerbose) /* spotless:on */ + + override fun toString(): String = + when { + transcription != null -> + "AudioTranscriptionCreateResponse{transcription=$transcription}" + transcriptionVerbose != null -> + "AudioTranscriptionCreateResponse{transcriptionVerbose=$transcriptionVerbose}" + _json != null -> "AudioTranscriptionCreateResponse{_unknown=$_json}" + else -> throw IllegalStateException("Invalid AudioTranscriptionCreateResponse") + } + + companion object { + + /** Represents a transcription response returned by model, based on the provided input. */ + @JvmStatic + fun ofTranscription(transcription: Transcription) = + AudioTranscriptionCreateResponse(transcription = transcription) + + /** + * Represents a verbose json transcription response returned by model, based on the provided + * input. + */ + @JvmStatic + fun ofTranscriptionVerbose(transcriptionVerbose: TranscriptionVerbose) = + AudioTranscriptionCreateResponse(transcriptionVerbose = transcriptionVerbose) + } + + /** + * An interface that defines how to map each variant of [AudioTranscriptionCreateResponse] to a + * value of type [T]. + */ + interface Visitor { + + /** Represents a transcription response returned by model, based on the provided input. */ + fun visitTranscription(transcription: Transcription): T + + /** + * Represents a verbose json transcription response returned by model, based on the provided + * input. + */ + fun visitTranscriptionVerbose(transcriptionVerbose: TranscriptionVerbose): T + + /** + * Maps an unknown variant of [AudioTranscriptionCreateResponse] to a value of type [T]. + * + * An instance of [AudioTranscriptionCreateResponse] can contain an unknown variant if it + * was deserialized from data that doesn't match any known variant. For example, if the SDK + * is on an older version than the API, then the API may respond with new variants that the + * SDK is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown AudioTranscriptionCreateResponse: $json") + } + } + + internal class Deserializer : + BaseDeserializer( + AudioTranscriptionCreateResponse::class + ) { + + override fun ObjectCodec.deserialize(node: JsonNode): AudioTranscriptionCreateResponse { + val json = JsonValue.fromJsonNode(node) + + tryDeserialize(node, jacksonTypeRef()) { it.validate() } + ?.let { + return AudioTranscriptionCreateResponse(transcription = it, _json = json) + } + tryDeserialize(node, jacksonTypeRef()) { it.validate() } + ?.let { + return AudioTranscriptionCreateResponse(transcriptionVerbose = it, _json = json) + } + + return AudioTranscriptionCreateResponse(_json = json) + } + } + + internal class Serializer : + BaseSerializer(AudioTranscriptionCreateResponse::class) { + + override fun serialize( + value: AudioTranscriptionCreateResponse, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.transcription != null -> generator.writeObject(value.transcription) + value.transcriptionVerbose != null -> + generator.writeObject(value.transcriptionVerbose) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid AudioTranscriptionCreateResponse") + } + } + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/AudioTranslationCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/AudioTranslationCreateParams.kt new file mode 100644 index 000000000..fe1ea3cfd --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/AudioTranslationCreateParams.kt @@ -0,0 +1,558 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonCreator +import com.openai.core.MultipartField +import com.openai.core.NoAutoDetect +import com.openai.core.Params +import com.openai.core.checkRequired +import com.openai.core.http.Headers +import com.openai.core.http.QueryParams +import com.openai.core.toImmutable +import java.util.Objects +import java.util.Optional + +/** Translates audio into English. */ +class AudioTranslationCreateParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, + * mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun file(): ByteArray = body.file() + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 + * model) is currently available. + */ + fun model(): AudioModel = body.model() + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should be in + * English. + */ + fun prompt(): Optional = body.prompt() + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, or + * `vtt`. + */ + fun responseFormat(): Optional = body.responseFormat() + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more + * random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, + * the model will use [log probability](https://en.wikipedia.org/wiki/Log_probability) to + * automatically increase the temperature until certain thresholds are hit. + */ + fun temperature(): Optional = body.temperature() + + /** + * The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, + * mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun _file(): MultipartField = body._file() + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 + * model) is currently available. + */ + fun _model(): MultipartField = body._model() + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should be in + * English. + */ + fun _prompt(): MultipartField = body._prompt() + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, or + * `vtt`. + */ + fun _responseFormat(): MultipartField = body._responseFormat() + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more + * random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, + * the model will use [log probability](https://en.wikipedia.org/wiki/Log_probability) to + * automatically increase the temperature until certain thresholds are hit. + */ + fun _temperature(): MultipartField = body._temperature() + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + @JvmSynthetic + internal fun _body(): Map> = + mapOf( + "file" to _file(), + "model" to _model(), + "prompt" to _prompt(), + "response_format" to _responseFormat(), + "temperature" to _temperature(), + ) + .toImmutable() + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + @NoAutoDetect + class Body + @JsonCreator + private constructor( + private val file: MultipartField, + private val model: MultipartField, + private val prompt: MultipartField, + private val responseFormat: MultipartField, + private val temperature: MultipartField, + ) { + + /** + * The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, + * mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun file(): ByteArray = file.value.getRequired("file") + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 + * model) is currently available. + */ + fun model(): AudioModel = model.value.getRequired("model") + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should be in + * English. + */ + fun prompt(): Optional = Optional.ofNullable(prompt.value.getNullable("prompt")) + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, + * or `vtt`. + */ + fun responseFormat(): Optional = + Optional.ofNullable(responseFormat.value.getNullable("response_format")) + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output + * more random, while lower values like 0.2 will make it more focused and deterministic. If + * set to 0, the model will use + * [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically + * increase the temperature until certain thresholds are hit. + */ + fun temperature(): Optional = + Optional.ofNullable(temperature.value.getNullable("temperature")) + + /** + * The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, + * mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun _file(): MultipartField = file + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 + * model) is currently available. + */ + fun _model(): MultipartField = model + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should be in + * English. + */ + fun _prompt(): MultipartField = prompt + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, + * or `vtt`. + */ + fun _responseFormat(): MultipartField = responseFormat + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output + * more random, while lower values like 0.2 will make it more focused and deterministic. If + * set to 0, the model will use + * [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically + * increase the temperature until certain thresholds are hit. + */ + fun _temperature(): MultipartField = temperature + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + file() + model() + prompt() + responseFormat() + temperature() + validated = true + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var file: MultipartField? = null + private var model: MultipartField? = null + private var prompt: MultipartField = MultipartField.of(null) + private var responseFormat: MultipartField = + MultipartField.of(null) + private var temperature: MultipartField = MultipartField.of(null) + + @JvmSynthetic + internal fun from(body: Body) = apply { + file = body.file + model = body.model + prompt = body.prompt + responseFormat = body.responseFormat + temperature = body.temperature + } + + /** + * The audio file object (not file name) translate, in one of these formats: flac, mp3, + * mp4, mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun file(file: ByteArray) = file(MultipartField.of(file)) + + /** + * The audio file object (not file name) translate, in one of these formats: flac, mp3, + * mp4, mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun file(file: MultipartField) = apply { this.file = file } + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper + * V2 model) is currently available. + */ + fun model(model: AudioModel) = model(MultipartField.of(model)) + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper + * V2 model) is currently available. + */ + fun model(model: MultipartField) = apply { this.model = model } + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper + * V2 model) is currently available. + */ + fun model(value: String) = model(AudioModel.of(value)) + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should be + * in English. + */ + fun prompt(prompt: String) = prompt(MultipartField.of(prompt)) + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should be + * in English. + */ + fun prompt(prompt: MultipartField) = apply { this.prompt = prompt } + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, + * `verbose_json`, or `vtt`. + */ + fun responseFormat(responseFormat: AudioResponseFormat) = + responseFormat(MultipartField.of(responseFormat)) + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, + * `verbose_json`, or `vtt`. + */ + fun responseFormat(responseFormat: MultipartField) = apply { + this.responseFormat = responseFormat + } + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + * output more random, while lower values like 0.2 will make it more focused and + * deterministic. If set to 0, the model will use + * [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically + * increase the temperature until certain thresholds are hit. + */ + fun temperature(temperature: Double) = temperature(MultipartField.of(temperature)) + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + * output more random, while lower values like 0.2 will make it more focused and + * deterministic. If set to 0, the model will use + * [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically + * increase the temperature until certain thresholds are hit. + */ + fun temperature(temperature: MultipartField) = apply { + this.temperature = temperature + } + + fun build(): Body = + Body( + checkRequired("file", file), + checkRequired("model", model), + prompt, + responseFormat, + temperature, + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && file == other.file && model == other.model && prompt == other.prompt && responseFormat == other.responseFormat && temperature == other.temperature /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(file, model, prompt, responseFormat, temperature) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{file=$file, model=$model, prompt=$prompt, responseFormat=$responseFormat, temperature=$temperature}" + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AudioTranslationCreateParams]. */ + @NoAutoDetect + class Builder internal constructor() { + + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(audioTranslationCreateParams: AudioTranslationCreateParams) = apply { + body = audioTranslationCreateParams.body.toBuilder() + additionalHeaders = audioTranslationCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = audioTranslationCreateParams.additionalQueryParams.toBuilder() + } + + /** + * The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, + * mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun file(file: ByteArray) = apply { body.file(file) } + + /** + * The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, + * mpeg, mpga, m4a, ogg, wav, or webm. + */ + fun file(file: MultipartField) = apply { body.file(file) } + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 + * model) is currently available. + */ + fun model(model: AudioModel) = apply { body.model(model) } + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 + * model) is currently available. + */ + fun model(model: MultipartField) = apply { body.model(model) } + + /** + * ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 + * model) is currently available. + */ + fun model(value: String) = apply { body.model(value) } + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should be in + * English. + */ + fun prompt(prompt: String) = apply { body.prompt(prompt) } + + /** + * An optional text to guide the model's style or continue a previous audio segment. The + * [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should be in + * English. + */ + fun prompt(prompt: MultipartField) = apply { body.prompt(prompt) } + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, + * or `vtt`. + */ + fun responseFormat(responseFormat: AudioResponseFormat) = apply { + body.responseFormat(responseFormat) + } + + /** + * The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, + * or `vtt`. + */ + fun responseFormat(responseFormat: MultipartField) = apply { + body.responseFormat(responseFormat) + } + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output + * more random, while lower values like 0.2 will make it more focused and deterministic. If + * set to 0, the model will use + * [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically + * increase the temperature until certain thresholds are hit. + */ + fun temperature(temperature: Double) = apply { body.temperature(temperature) } + + /** + * The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output + * more random, while lower values like 0.2 will make it more focused and deterministic. If + * set to 0, the model will use + * [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically + * increase the temperature until certain thresholds are hit. + */ + fun temperature(temperature: MultipartField) = apply { + body.temperature(temperature) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + fun build(): AudioTranslationCreateParams = + AudioTranslationCreateParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is AudioTranslationCreateParams && body == other.body && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(body, additionalHeaders, additionalQueryParams) /* spotless:on */ + + override fun toString() = + "AudioTranslationCreateParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/AudioTranslationCreateResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/AudioTranslationCreateResponse.kt new file mode 100644 index 000000000..4486c04e0 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/AudioTranslationCreateResponse.kt @@ -0,0 +1,163 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.BaseDeserializer +import com.openai.core.BaseSerializer +import com.openai.core.JsonValue +import com.openai.core.getOrThrow +import com.openai.errors.OpenAIInvalidDataException +import java.util.Objects +import java.util.Optional + +@JsonDeserialize(using = AudioTranslationCreateResponse.Deserializer::class) +@JsonSerialize(using = AudioTranslationCreateResponse.Serializer::class) +class AudioTranslationCreateResponse +private constructor( + private val translation: Translation? = null, + private val translationVerbose: TranslationVerbose? = null, + private val _json: JsonValue? = null, +) { + + fun translation(): Optional = Optional.ofNullable(translation) + + fun translationVerbose(): Optional = Optional.ofNullable(translationVerbose) + + fun isTranslation(): Boolean = translation != null + + fun isTranslationVerbose(): Boolean = translationVerbose != null + + fun asTranslation(): Translation = translation.getOrThrow("translation") + + fun asTranslationVerbose(): TranslationVerbose = + translationVerbose.getOrThrow("translationVerbose") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T { + return when { + translation != null -> visitor.visitTranslation(translation) + translationVerbose != null -> visitor.visitTranslationVerbose(translationVerbose) + else -> visitor.unknown(_json) + } + } + + private var validated: Boolean = false + + fun validate(): AudioTranslationCreateResponse = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitTranslation(translation: Translation) { + translation.validate() + } + + override fun visitTranslationVerbose(translationVerbose: TranslationVerbose) { + translationVerbose.validate() + } + } + ) + validated = true + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is AudioTranslationCreateResponse && translation == other.translation && translationVerbose == other.translationVerbose /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(translation, translationVerbose) /* spotless:on */ + + override fun toString(): String = + when { + translation != null -> "AudioTranslationCreateResponse{translation=$translation}" + translationVerbose != null -> + "AudioTranslationCreateResponse{translationVerbose=$translationVerbose}" + _json != null -> "AudioTranslationCreateResponse{_unknown=$_json}" + else -> throw IllegalStateException("Invalid AudioTranslationCreateResponse") + } + + companion object { + + @JvmStatic + fun ofTranslation(translation: Translation) = + AudioTranslationCreateResponse(translation = translation) + + @JvmStatic + fun ofTranslationVerbose(translationVerbose: TranslationVerbose) = + AudioTranslationCreateResponse(translationVerbose = translationVerbose) + } + + /** + * An interface that defines how to map each variant of [AudioTranslationCreateResponse] to a + * value of type [T]. + */ + interface Visitor { + + fun visitTranslation(translation: Translation): T + + fun visitTranslationVerbose(translationVerbose: TranslationVerbose): T + + /** + * Maps an unknown variant of [AudioTranslationCreateResponse] to a value of type [T]. + * + * An instance of [AudioTranslationCreateResponse] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if the SDK is + * on an older version than the API, then the API may respond with new variants that the SDK + * is unaware of. + * + * @throws OpenAIInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown AudioTranslationCreateResponse: $json") + } + } + + internal class Deserializer : + BaseDeserializer(AudioTranslationCreateResponse::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): AudioTranslationCreateResponse { + val json = JsonValue.fromJsonNode(node) + + tryDeserialize(node, jacksonTypeRef()) { it.validate() } + ?.let { + return AudioTranslationCreateResponse(translation = it, _json = json) + } + tryDeserialize(node, jacksonTypeRef()) { it.validate() } + ?.let { + return AudioTranslationCreateResponse(translationVerbose = it, _json = json) + } + + return AudioTranslationCreateResponse(_json = json) + } + } + + internal class Serializer : + BaseSerializer(AudioTranslationCreateResponse::class) { + + override fun serialize( + value: AudioTranslationCreateResponse, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.translation != null -> generator.writeObject(value.translation) + value.translationVerbose != null -> generator.writeObject(value.translationVerbose) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid AudioTranslationCreateResponse") + } + } + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/Batch.kt b/openai-java-core/src/main/kotlin/com/openai/models/Batch.kt index 3490df2de..902e1187a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/Batch.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/Batch.kt @@ -12,6 +12,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -720,14 +721,8 @@ private constructor( fun addData(data: BatchError) = apply { this.data = - (this.data ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(data) + (this.data ?: JsonField.of(mutableListOf())).also { + checkKnown("data", it).add(data) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/BetaAssistantCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/BetaAssistantCreateParams.kt index 954b422c5..24948a0ae 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/BetaAssistantCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/BetaAssistantCreateParams.kt @@ -13,6 +13,7 @@ import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect import com.openai.core.Params +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams @@ -890,14 +891,8 @@ private constructor( */ fun addTool(tool: AssistantTool) = apply { tools = - (tools ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(tool) + (tools ?: JsonField.of(mutableListOf())).also { + checkKnown("tools", it).add(tool) } } @@ -1881,14 +1876,8 @@ private constructor( */ fun addFileId(fileId: String) = apply { fileIds = - (fileIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(fileId) + (fileIds ?: JsonField.of(mutableListOf())).also { + checkKnown("fileIds", it).add(fileId) } } @@ -2055,14 +2044,8 @@ private constructor( */ fun addVectorStoreId(vectorStoreId: String) = apply { vectorStoreIds = - (vectorStoreIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(vectorStoreId) + (vectorStoreIds ?: JsonField.of(mutableListOf())).also { + checkKnown("vectorStoreIds", it).add(vectorStoreId) } } @@ -2093,14 +2076,8 @@ private constructor( */ fun addVectorStore(vectorStore: VectorStore) = apply { vectorStores = - (vectorStores ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(vectorStore) + (vectorStores ?: JsonField.of(mutableListOf())).also { + checkKnown("vectorStores", it).add(vectorStore) } } @@ -2308,14 +2285,8 @@ private constructor( */ fun addFileId(fileId: String) = apply { fileIds = - (fileIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(fileId) + (fileIds ?: JsonField.of(mutableListOf())).also { + checkKnown("fileIds", it).add(fileId) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/BetaAssistantUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/BetaAssistantUpdateParams.kt index b5f6d7448..324c5d930 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/BetaAssistantUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/BetaAssistantUpdateParams.kt @@ -13,6 +13,7 @@ import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect import com.openai.core.Params +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams @@ -900,14 +901,8 @@ private constructor( */ fun addTool(tool: AssistantTool) = apply { tools = - (tools ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(tool) + (tools ?: JsonField.of(mutableListOf())).also { + checkKnown("tools", it).add(tool) } } @@ -2171,14 +2166,8 @@ private constructor( */ fun addFileId(fileId: String) = apply { fileIds = - (fileIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(fileId) + (fileIds ?: JsonField.of(mutableListOf())).also { + checkKnown("fileIds", it).add(fileId) } } @@ -2320,14 +2309,8 @@ private constructor( */ fun addVectorStoreId(vectorStoreId: String) = apply { vectorStoreIds = - (vectorStoreIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(vectorStoreId) + (vectorStoreIds ?: JsonField.of(mutableListOf())).also { + checkKnown("vectorStoreIds", it).add(vectorStoreId) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadCreateAndRunParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadCreateAndRunParams.kt index cfb5915fd..1916afbf1 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadCreateAndRunParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadCreateAndRunParams.kt @@ -22,6 +22,7 @@ import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect import com.openai.core.Params +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.http.Headers @@ -1206,14 +1207,8 @@ private constructor( */ fun addTool(tool: Tool) = apply { tools = - (tools ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(tool) + (tools ?: JsonField.of(mutableListOf())).also { + checkKnown("tools", it).add(tool) } } @@ -2185,14 +2180,8 @@ private constructor( */ fun addMessage(message: Message) = apply { messages = - (messages ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(message) + (messages ?: JsonField.of(mutableListOf())).also { + checkKnown("messages", it).add(message) } } @@ -2461,14 +2450,8 @@ private constructor( */ fun addAttachment(attachment: Attachment) = apply { attachments = - (attachments ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(attachment) + (attachments ?: JsonField.of(mutableListOf())).also { + checkKnown("attachments", it).add(attachment) } } @@ -2898,14 +2881,8 @@ private constructor( /** The tools to add this file to. */ fun addTool(tool: Tool) = apply { tools = - (tools ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(tool) + (tools ?: JsonField.of(mutableListOf())).also { + checkKnown("tools", it).add(tool) } } @@ -3352,14 +3329,8 @@ private constructor( */ fun addFileId(fileId: String) = apply { fileIds = - (fileIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(fileId) + (fileIds ?: JsonField.of(mutableListOf())).also { + checkKnown("fileIds", it).add(fileId) } } @@ -3526,14 +3497,8 @@ private constructor( */ fun addVectorStoreId(vectorStoreId: String) = apply { vectorStoreIds = - (vectorStoreIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(vectorStoreId) + (vectorStoreIds ?: JsonField.of(mutableListOf())).also { + checkKnown("vectorStoreIds", it).add(vectorStoreId) } } @@ -3564,14 +3529,8 @@ private constructor( */ fun addVectorStore(vectorStore: VectorStore) = apply { vectorStores = - (vectorStores ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(vectorStore) + (vectorStores ?: JsonField.of(mutableListOf())).also { + checkKnown("vectorStores", it).add(vectorStore) } } @@ -3783,14 +3742,8 @@ private constructor( */ fun addFileId(fileId: String) = apply { fileIds = - (fileIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(fileId) + (fileIds ?: JsonField.of(mutableListOf())).also { + checkKnown("fileIds", it).add(fileId) } } @@ -4120,14 +4073,8 @@ private constructor( */ fun addFileId(fileId: String) = apply { fileIds = - (fileIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(fileId) + (fileIds ?: JsonField.of(mutableListOf())).also { + checkKnown("fileIds", it).add(fileId) } } @@ -4269,14 +4216,8 @@ private constructor( */ fun addVectorStoreId(vectorStoreId: String) = apply { vectorStoreIds = - (vectorStoreIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(vectorStoreId) + (vectorStoreIds ?: JsonField.of(mutableListOf())).also { + checkKnown("vectorStoreIds", it).add(vectorStoreId) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadCreateParams.kt index 0c72cf4d9..5426215d4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadCreateParams.kt @@ -22,6 +22,7 @@ import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect import com.openai.core.Params +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.http.Headers @@ -234,14 +235,8 @@ private constructor( */ fun addMessage(message: Message) = apply { messages = - (messages ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(message) + (messages ?: JsonField.of(mutableListOf())).also { + checkKnown("messages", it).add(message) } } @@ -740,14 +735,8 @@ private constructor( /** A list of files attached to the message, and the tools they should be added to. */ fun addAttachment(attachment: Attachment) = apply { attachments = - (attachments ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(attachment) + (attachments ?: JsonField.of(mutableListOf())).also { + checkKnown("attachments", it).add(attachment) } } @@ -1167,14 +1156,8 @@ private constructor( /** The tools to add this file to. */ fun addTool(tool: Tool) = apply { tools = - (tools ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(tool) + (tools ?: JsonField.of(mutableListOf())).also { + checkKnown("tools", it).add(tool) } } @@ -1613,14 +1596,8 @@ private constructor( */ fun addFileId(fileId: String) = apply { fileIds = - (fileIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(fileId) + (fileIds ?: JsonField.of(mutableListOf())).also { + checkKnown("fileIds", it).add(fileId) } } @@ -1787,14 +1764,8 @@ private constructor( */ fun addVectorStoreId(vectorStoreId: String) = apply { vectorStoreIds = - (vectorStoreIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(vectorStoreId) + (vectorStoreIds ?: JsonField.of(mutableListOf())).also { + checkKnown("vectorStoreIds", it).add(vectorStoreId) } } @@ -1825,14 +1796,8 @@ private constructor( */ fun addVectorStore(vectorStore: VectorStore) = apply { vectorStores = - (vectorStores ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(vectorStore) + (vectorStores ?: JsonField.of(mutableListOf())).also { + checkKnown("vectorStores", it).add(vectorStore) } } @@ -2040,14 +2005,8 @@ private constructor( */ fun addFileId(fileId: String) = apply { fileIds = - (fileIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(fileId) + (fileIds ?: JsonField.of(mutableListOf())).also { + checkKnown("fileIds", it).add(fileId) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadMessageCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadMessageCreateParams.kt index cf9a18987..c18f842b2 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadMessageCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadMessageCreateParams.kt @@ -22,6 +22,7 @@ import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect import com.openai.core.Params +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.http.Headers @@ -278,14 +279,8 @@ private constructor( /** A list of files attached to the message, and the tools they should be added to. */ fun addAttachment(attachment: Attachment) = apply { attachments = - (attachments ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(attachment) + (attachments ?: JsonField.of(mutableListOf())).also { + checkKnown("attachments", it).add(attachment) } } @@ -952,14 +947,8 @@ private constructor( /** The tools to add this file to. */ fun addTool(tool: Tool) = apply { tools = - (tools ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(tool) + (tools ?: JsonField.of(mutableListOf())).also { + checkKnown("tools", it).add(tool) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadRunCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadRunCreateParams.kt index 24af9ed10..366e0609a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadRunCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadRunCreateParams.kt @@ -22,6 +22,7 @@ import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect import com.openai.core.Params +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.http.Headers @@ -833,14 +834,8 @@ private constructor( /** Adds additional messages to the thread before creating the run. */ fun addAdditionalMessage(additionalMessage: AdditionalMessage) = apply { additionalMessages = - (additionalMessages ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(additionalMessage) + (additionalMessages ?: JsonField.of(mutableListOf())).also { + checkKnown("additionalMessages", it).add(additionalMessage) } } @@ -1327,14 +1322,8 @@ private constructor( */ fun addTool(tool: AssistantTool) = apply { tools = - (tools ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(tool) + (tools ?: JsonField.of(mutableListOf())).also { + checkKnown("tools", it).add(tool) } } @@ -2441,14 +2430,8 @@ private constructor( /** A list of files attached to the message, and the tools they should be added to. */ fun addAttachment(attachment: Attachment) = apply { attachments = - (attachments ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(attachment) + (attachments ?: JsonField.of(mutableListOf())).also { + checkKnown("attachments", it).add(attachment) } } @@ -2868,14 +2851,8 @@ private constructor( /** The tools to add this file to. */ fun addTool(tool: Tool) = apply { tools = - (tools ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(tool) + (tools ?: JsonField.of(mutableListOf())).also { + checkKnown("tools", it).add(tool) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadRunSubmitToolOutputsParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadRunSubmitToolOutputsParams.kt index 053eabf0a..a6bdea965 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadRunSubmitToolOutputsParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadRunSubmitToolOutputsParams.kt @@ -12,6 +12,7 @@ import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect import com.openai.core.Params +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams @@ -128,14 +129,8 @@ private constructor( /** A list of tools for which the outputs are being submitted. */ fun addToolOutput(toolOutput: ToolOutput) = apply { toolOutputs = - (toolOutputs ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(toolOutput) + (toolOutputs ?: JsonField.of(mutableListOf())).also { + checkKnown("toolOutputs", it).add(toolOutput) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadUpdateParams.kt index eeebc3f69..ba4e3a291 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/BetaThreadUpdateParams.kt @@ -12,6 +12,7 @@ import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect import com.openai.core.Params +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams @@ -674,14 +675,8 @@ private constructor( */ fun addFileId(fileId: String) = apply { fileIds = - (fileIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(fileId) + (fileIds ?: JsonField.of(mutableListOf())).also { + checkKnown("fileIds", it).add(fileId) } } @@ -823,14 +818,8 @@ private constructor( */ fun addVectorStoreId(vectorStoreId: String) = apply { vectorStoreIds = - (vectorStoreIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(vectorStoreId) + (vectorStoreIds ?: JsonField.of(mutableListOf())).also { + checkKnown("vectorStoreIds", it).add(vectorStoreId) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/BetaVectorStoreCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/BetaVectorStoreCreateParams.kt index b7a68e1f6..f4583b246 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/BetaVectorStoreCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/BetaVectorStoreCreateParams.kt @@ -12,6 +12,7 @@ import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect import com.openai.core.Params +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams @@ -294,14 +295,8 @@ private constructor( */ fun addFileId(fileId: String) = apply { fileIds = - (fileIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(fileId) + (fileIds ?: JsonField.of(mutableListOf())).also { + checkKnown("fileIds", it).add(fileId) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/BetaVectorStoreFileBatchCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/BetaVectorStoreFileBatchCreateParams.kt index 54f0534c8..d94d823cd 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/BetaVectorStoreFileBatchCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/BetaVectorStoreFileBatchCreateParams.kt @@ -12,6 +12,7 @@ import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect import com.openai.core.Params +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams @@ -172,14 +173,8 @@ private constructor( */ fun addFileId(fileId: String) = apply { fileIds = - (fileIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(fileId) + (fileIds ?: JsonField.of(mutableListOf())).also { + checkKnown("fileIds", it).add(fileId) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletion.kt b/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletion.kt index 53865268f..9d6a542ba 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletion.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletion.kt @@ -12,6 +12,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -182,14 +183,8 @@ private constructor( /** A list of chat completion choices. Can be more than one if `n` is greater than 1. */ fun addChoice(choice: Choice) = apply { choices = - (choices ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(choice) + (choices ?: JsonField.of(mutableListOf())).also { + checkKnown("choices", it).add(choice) } } @@ -667,14 +662,8 @@ private constructor( /** A list of message content tokens with log probability information. */ fun addContent(content: ChatCompletionTokenLogprob) = apply { this.content = - (this.content ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(content) + (this.content ?: JsonField.of(mutableListOf())).also { + checkKnown("content", it).add(content) } } @@ -694,14 +683,8 @@ private constructor( /** A list of message refusal tokens with log probability information. */ fun addRefusal(refusal: ChatCompletionTokenLogprob) = apply { this.refusal = - (this.refusal ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(refusal) + (this.refusal ?: JsonField.of(mutableListOf())).also { + checkKnown("refusal", it).add(refusal) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionAssistantMessageParam.kt b/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionAssistantMessageParam.kt index 20ae74e30..312d18902 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionAssistantMessageParam.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionAssistantMessageParam.kt @@ -20,6 +20,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.immutableEmptyMap @@ -290,14 +291,8 @@ private constructor( /** The tool calls generated by the model, such as function calls. */ fun addToolCall(toolCall: ChatCompletionMessageToolCall) = apply { toolCalls = - (toolCalls ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(toolCall) + (toolCalls ?: JsonField.of(mutableListOf())).also { + checkKnown("toolCalls", it).add(toolCall) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionChunk.kt b/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionChunk.kt index cd584e5f7..f84f58828 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionChunk.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionChunk.kt @@ -12,6 +12,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -217,14 +218,8 @@ private constructor( */ fun addChoice(choice: Choice) = apply { choices = - (choices ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(choice) + (choices ?: JsonField.of(mutableListOf())).also { + checkKnown("choices", it).add(choice) } } @@ -675,14 +670,8 @@ private constructor( fun addToolCall(toolCall: ToolCall) = apply { toolCalls = - (toolCalls ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(toolCall) + (toolCalls ?: JsonField.of(mutableListOf())).also { + checkKnown("toolCalls", it).add(toolCall) } } @@ -1624,14 +1613,8 @@ private constructor( /** A list of message content tokens with log probability information. */ fun addContent(content: ChatCompletionTokenLogprob) = apply { this.content = - (this.content ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(content) + (this.content ?: JsonField.of(mutableListOf())).also { + checkKnown("content", it).add(content) } } @@ -1651,14 +1634,8 @@ private constructor( /** A list of message refusal tokens with log probability information. */ fun addRefusal(refusal: ChatCompletionTokenLogprob) = apply { this.refusal = - (this.refusal ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(refusal) + (this.refusal ?: JsonField.of(mutableListOf())).also { + checkKnown("refusal", it).add(refusal) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionCreateParams.kt index 6bb970afd..65fbe332e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionCreateParams.kt @@ -22,6 +22,7 @@ import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect import com.openai.core.Params +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.http.Headers @@ -1366,14 +1367,8 @@ private constructor( */ fun addMessage(message: ChatCompletionMessageParam) = apply { messages = - (messages ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(message) + (messages ?: JsonField.of(mutableListOf())).also { + checkKnown("messages", it).add(message) } } @@ -1655,14 +1650,8 @@ private constructor( @Deprecated("deprecated") fun addFunction(function: Function) = apply { functions = - (functions ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(function) + (functions ?: JsonField.of(mutableListOf())).also { + checkKnown("functions", it).add(function) } } @@ -1899,14 +1888,8 @@ private constructor( */ fun addModality(modality: ChatCompletionModality) = apply { modalities = - (modalities ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(modality) + (modalities ?: JsonField.of(mutableListOf())).also { + checkKnown("modalities", it).add(modality) } } @@ -2382,14 +2365,8 @@ private constructor( */ fun addTool(tool: ChatCompletionTool) = apply { tools = - (tools ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(tool) + (tools ?: JsonField.of(mutableListOf())).also { + checkKnown("tools", it).add(tool) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionMessage.kt b/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionMessage.kt index 1edec00b7..d7da00c38 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionMessage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionMessage.kt @@ -11,6 +11,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -235,14 +236,8 @@ private constructor( /** The tool calls generated by the model, such as function calls. */ fun addToolCall(toolCall: ChatCompletionMessageToolCall) = apply { toolCalls = - (toolCalls ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(toolCall) + (toolCalls ?: JsonField.of(mutableListOf())).also { + checkKnown("toolCalls", it).add(toolCall) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionStoreMessage.kt b/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionStoreMessage.kt index d3615cfba..3d2755641 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionStoreMessage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionStoreMessage.kt @@ -11,6 +11,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -236,14 +237,8 @@ private constructor( /** The tool calls generated by the model, such as function calls. */ fun addToolCall(toolCall: ChatCompletionMessageToolCall) = apply { toolCalls = - (toolCalls ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(toolCall) + (toolCalls ?: JsonField.of(mutableListOf())).also { + checkKnown("toolCalls", it).add(toolCall) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionTokenLogprob.kt b/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionTokenLogprob.kt index 87648b585..6a43736c7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionTokenLogprob.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ChatCompletionTokenLogprob.kt @@ -11,6 +11,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -165,15 +166,7 @@ private constructor( */ fun addByte(byte_: Long) = apply { bytes = - (bytes ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(byte_) - } + (bytes ?: JsonField.of(mutableListOf())).also { checkKnown("bytes", it).add(byte_) } } /** @@ -208,14 +201,8 @@ private constructor( */ fun addTopLogprob(topLogprob: TopLogprob) = apply { topLogprobs = - (topLogprobs ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(topLogprob) + (topLogprobs ?: JsonField.of(mutableListOf())).also { + checkKnown("topLogprobs", it).add(topLogprob) } } @@ -379,14 +366,8 @@ private constructor( */ fun addByte(byte_: Long) = apply { bytes = - (bytes ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(byte_) + (bytes ?: JsonField.of(mutableListOf())).also { + checkKnown("bytes", it).add(byte_) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/CodeInterpreterToolCall.kt b/openai-java-core/src/main/kotlin/com/openai/models/CodeInterpreterToolCall.kt index 1210b30f0..ca1b5a981 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/CodeInterpreterToolCall.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/CodeInterpreterToolCall.kt @@ -20,6 +20,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.immutableEmptyMap @@ -256,14 +257,8 @@ private constructor( */ fun addOutput(output: Output) = apply { outputs = - (outputs ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(output) + (outputs ?: JsonField.of(mutableListOf())).also { + checkKnown("outputs", it).add(output) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/CodeInterpreterToolCallDelta.kt b/openai-java-core/src/main/kotlin/com/openai/models/CodeInterpreterToolCallDelta.kt index 0ea5289eb..bd43e7fba 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/CodeInterpreterToolCallDelta.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/CodeInterpreterToolCallDelta.kt @@ -20,6 +20,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.immutableEmptyMap @@ -274,14 +275,8 @@ private constructor( */ fun addOutput(output: Output) = apply { outputs = - (outputs ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(output) + (outputs ?: JsonField.of(mutableListOf())).also { + checkKnown("outputs", it).add(output) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/Completion.kt b/openai-java-core/src/main/kotlin/com/openai/models/Completion.kt index 0e9db0105..0a1563927 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/Completion.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/Completion.kt @@ -11,6 +11,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -171,14 +172,8 @@ private constructor( /** The list of completion choices the model generated for the input prompt. */ fun addChoice(choice: CompletionChoice) = apply { choices = - (choices ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(choice) + (choices ?: JsonField.of(mutableListOf())).also { + checkKnown("choices", it).add(choice) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/CompletionChoice.kt b/openai-java-core/src/main/kotlin/com/openai/models/CompletionChoice.kt index 3008a794d..db481b2e8 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/CompletionChoice.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/CompletionChoice.kt @@ -12,6 +12,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -377,14 +378,8 @@ private constructor( fun addTextOffset(textOffset: Long) = apply { this.textOffset = - (this.textOffset ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(textOffset) + (this.textOffset ?: JsonField.of(mutableListOf())).also { + checkKnown("textOffset", it).add(textOffset) } } @@ -397,14 +392,8 @@ private constructor( fun addTokenLogprob(tokenLogprob: Double) = apply { tokenLogprobs = - (tokenLogprobs ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(tokenLogprob) + (tokenLogprobs ?: JsonField.of(mutableListOf())).also { + checkKnown("tokenLogprobs", it).add(tokenLogprob) } } @@ -416,14 +405,8 @@ private constructor( fun addToken(token: String) = apply { tokens = - (tokens ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(token) + (tokens ?: JsonField.of(mutableListOf())).also { + checkKnown("tokens", it).add(token) } } @@ -435,14 +418,8 @@ private constructor( fun addTopLogprob(topLogprob: TopLogprob) = apply { topLogprobs = - (topLogprobs ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(topLogprob) + (topLogprobs ?: JsonField.of(mutableListOf())).also { + checkKnown("topLogprobs", it).add(topLogprob) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/CreateEmbeddingResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/CreateEmbeddingResponse.kt index 48bc376e1..9680ca910 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/CreateEmbeddingResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/CreateEmbeddingResponse.kt @@ -11,6 +11,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -109,14 +110,8 @@ private constructor( /** The list of embeddings generated by the model. */ fun addData(data: Embedding) = apply { this.data = - (this.data ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(data) + (this.data ?: JsonField.of(mutableListOf())).also { + checkKnown("data", it).add(data) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/Embedding.kt b/openai-java-core/src/main/kotlin/com/openai/models/Embedding.kt index 5d64ae936..171317c65 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/Embedding.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/Embedding.kt @@ -11,6 +11,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -118,14 +119,8 @@ private constructor( */ fun addEmbedding(embedding: Double) = apply { this.embedding = - (this.embedding ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(embedding) + (this.embedding ?: JsonField.of(mutableListOf())).also { + checkKnown("embedding", it).add(embedding) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/FileCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/FileCreateParams.kt new file mode 100644 index 000000000..6a50b282a --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/FileCreateParams.kt @@ -0,0 +1,364 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonCreator +import com.openai.core.MultipartField +import com.openai.core.NoAutoDetect +import com.openai.core.Params +import com.openai.core.checkRequired +import com.openai.core.http.Headers +import com.openai.core.http.QueryParams +import com.openai.core.toImmutable +import java.util.Objects + +/** + * Upload a file that can be used across various endpoints. Individual files can be up to 512 MB, + * and the size of all files uploaded by one organization can be up to 100 GB. + * + * The Assistants API supports files up to 2 million tokens and of specific file types. See the + * [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) for details. + * + * The Fine-tuning API only supports `.jsonl` files. The input also has certain required formats for + * fine-tuning [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) or + * [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) + * models. + * + * The Batch API only supports `.jsonl` files up to 200 MB in size. The input also has a specific + * required [format](https://platform.openai.com/docs/api-reference/batch/request-input). + * + * Please [contact us](https://help.openai.com/) if you need to increase these storage limits. + */ +class FileCreateParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** The File object (not file name) to be uploaded. */ + fun file(): ByteArray = body.file() + + /** + * The intended purpose of the uploaded file. + * + * Use "assistants" for [Assistants](https://platform.openai.com/docs/api-reference/assistants) + * and [Message](https://platform.openai.com/docs/api-reference/messages) files, "vision" for + * Assistants image file inputs, "batch" for + * [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for + * [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). + */ + fun purpose(): FilePurpose = body.purpose() + + /** The File object (not file name) to be uploaded. */ + fun _file(): MultipartField = body._file() + + /** + * The intended purpose of the uploaded file. + * + * Use "assistants" for [Assistants](https://platform.openai.com/docs/api-reference/assistants) + * and [Message](https://platform.openai.com/docs/api-reference/messages) files, "vision" for + * Assistants image file inputs, "batch" for + * [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for + * [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). + */ + fun _purpose(): MultipartField = body._purpose() + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + @JvmSynthetic + internal fun _body(): Map> = + mapOf("file" to _file(), "purpose" to _purpose()).toImmutable() + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + @NoAutoDetect + class Body + @JsonCreator + private constructor( + private val file: MultipartField, + private val purpose: MultipartField, + ) { + + /** The File object (not file name) to be uploaded. */ + fun file(): ByteArray = file.value.getRequired("file") + + /** + * The intended purpose of the uploaded file. + * + * Use "assistants" for + * [Assistants](https://platform.openai.com/docs/api-reference/assistants) and + * [Message](https://platform.openai.com/docs/api-reference/messages) files, "vision" for + * Assistants image file inputs, "batch" for + * [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for + * [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). + */ + fun purpose(): FilePurpose = purpose.value.getRequired("purpose") + + /** The File object (not file name) to be uploaded. */ + fun _file(): MultipartField = file + + /** + * The intended purpose of the uploaded file. + * + * Use "assistants" for + * [Assistants](https://platform.openai.com/docs/api-reference/assistants) and + * [Message](https://platform.openai.com/docs/api-reference/messages) files, "vision" for + * Assistants image file inputs, "batch" for + * [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for + * [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). + */ + fun _purpose(): MultipartField = purpose + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + file() + purpose() + validated = true + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var file: MultipartField? = null + private var purpose: MultipartField? = null + + @JvmSynthetic + internal fun from(body: Body) = apply { + file = body.file + purpose = body.purpose + } + + /** The File object (not file name) to be uploaded. */ + fun file(file: ByteArray) = file(MultipartField.of(file)) + + /** The File object (not file name) to be uploaded. */ + fun file(file: MultipartField) = apply { this.file = file } + + /** + * The intended purpose of the uploaded file. + * + * Use "assistants" for + * [Assistants](https://platform.openai.com/docs/api-reference/assistants) and + * [Message](https://platform.openai.com/docs/api-reference/messages) files, "vision" + * for Assistants image file inputs, "batch" for + * [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for + * [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). + */ + fun purpose(purpose: FilePurpose) = purpose(MultipartField.of(purpose)) + + /** + * The intended purpose of the uploaded file. + * + * Use "assistants" for + * [Assistants](https://platform.openai.com/docs/api-reference/assistants) and + * [Message](https://platform.openai.com/docs/api-reference/messages) files, "vision" + * for Assistants image file inputs, "batch" for + * [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for + * [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). + */ + fun purpose(purpose: MultipartField) = apply { this.purpose = purpose } + + fun build(): Body = Body(checkRequired("file", file), checkRequired("purpose", purpose)) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && file == other.file && purpose == other.purpose /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(file, purpose) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Body{file=$file, purpose=$purpose}" + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [FileCreateParams]. */ + @NoAutoDetect + class Builder internal constructor() { + + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(fileCreateParams: FileCreateParams) = apply { + body = fileCreateParams.body.toBuilder() + additionalHeaders = fileCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = fileCreateParams.additionalQueryParams.toBuilder() + } + + /** The File object (not file name) to be uploaded. */ + fun file(file: ByteArray) = apply { body.file(file) } + + /** The File object (not file name) to be uploaded. */ + fun file(file: MultipartField) = apply { body.file(file) } + + /** + * The intended purpose of the uploaded file. + * + * Use "assistants" for + * [Assistants](https://platform.openai.com/docs/api-reference/assistants) and + * [Message](https://platform.openai.com/docs/api-reference/messages) files, "vision" for + * Assistants image file inputs, "batch" for + * [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for + * [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). + */ + fun purpose(purpose: FilePurpose) = apply { body.purpose(purpose) } + + /** + * The intended purpose of the uploaded file. + * + * Use "assistants" for + * [Assistants](https://platform.openai.com/docs/api-reference/assistants) and + * [Message](https://platform.openai.com/docs/api-reference/messages) files, "vision" for + * Assistants image file inputs, "batch" for + * [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for + * [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). + */ + fun purpose(purpose: MultipartField) = apply { body.purpose(purpose) } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + fun build(): FileCreateParams = + FileCreateParams(body.build(), additionalHeaders.build(), additionalQueryParams.build()) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is FileCreateParams && body == other.body && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(body, additionalHeaders, additionalQueryParams) /* spotless:on */ + + override fun toString() = + "FileCreateParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/FileSearchToolCall.kt b/openai-java-core/src/main/kotlin/com/openai/models/FileSearchToolCall.kt index d375d316e..68ea033ee 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/FileSearchToolCall.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/FileSearchToolCall.kt @@ -12,6 +12,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -227,14 +228,8 @@ private constructor( /** The results of the file search. */ fun addResult(result: Result) = apply { results = - (results ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(result) + (results ?: JsonField.of(mutableListOf())).also { + checkKnown("results", it).add(result) } } @@ -549,14 +544,8 @@ private constructor( */ fun addContent(content: Content) = apply { this.content = - (this.content ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(content) + (this.content ?: JsonField.of(mutableListOf())).also { + checkKnown("content", it).add(content) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/FineTuningJob.kt b/openai-java-core/src/main/kotlin/com/openai/models/FineTuningJob.kt index dd8147215..5a9f47d4b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/FineTuningJob.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/FineTuningJob.kt @@ -21,6 +21,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.immutableEmptyMap @@ -494,14 +495,8 @@ private constructor( */ fun addResultFile(resultFile: String) = apply { resultFiles = - (resultFiles ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(resultFile) + (resultFiles ?: JsonField.of(mutableListOf())).also { + checkKnown("resultFiles", it).add(resultFile) } } @@ -633,14 +628,8 @@ private constructor( /** A list of integrations to enable for this fine-tuning job. */ fun addIntegration(integration: FineTuningJobWandbIntegrationObject) = apply { integrations = - (integrations ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(integration) + (integrations ?: JsonField.of(mutableListOf())).also { + checkKnown("integrations", it).add(integration) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/FineTuningJobCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/FineTuningJobCreateParams.kt index 70dd0f3ee..7588bc366 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/FineTuningJobCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/FineTuningJobCreateParams.kt @@ -22,6 +22,7 @@ import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect import com.openai.core.Params +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.http.Headers @@ -530,14 +531,8 @@ private constructor( /** A list of integrations to enable for your fine-tuning job. */ fun addIntegration(integration: Integration) = apply { integrations = - (integrations ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(integration) + (integrations ?: JsonField.of(mutableListOf())).also { + checkKnown("integrations", it).add(integration) } } @@ -2152,14 +2147,8 @@ private constructor( */ fun addTag(tag: String) = apply { tags = - (tags ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(tag) + (tags ?: JsonField.of(mutableListOf())).also { + checkKnown("tags", it).add(tag) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/FineTuningJobWandbIntegration.kt b/openai-java-core/src/main/kotlin/com/openai/models/FineTuningJobWandbIntegration.kt index a3e382d72..2bcfa5a8b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/FineTuningJobWandbIntegration.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/FineTuningJobWandbIntegration.kt @@ -11,6 +11,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -180,16 +181,7 @@ private constructor( * "openai/{base-model}", "openai/{ftjob-abcdef}". */ fun addTag(tag: String) = apply { - tags = - (tags ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(tag) - } + tags = (tags ?: JsonField.of(mutableListOf())).also { checkKnown("tags", it).add(tag) } } fun additionalProperties(additionalProperties: Map) = apply { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ImageCreateVariationParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/ImageCreateVariationParams.kt new file mode 100644 index 000000000..d848d9dc7 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/ImageCreateVariationParams.kt @@ -0,0 +1,808 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonCreator +import com.openai.core.Enum +import com.openai.core.JsonField +import com.openai.core.MultipartField +import com.openai.core.NoAutoDetect +import com.openai.core.Params +import com.openai.core.checkRequired +import com.openai.core.http.Headers +import com.openai.core.http.QueryParams +import com.openai.core.toImmutable +import com.openai.errors.OpenAIInvalidDataException +import java.util.Objects +import java.util.Optional + +/** Creates a variation of a given image. */ +class ImageCreateVariationParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * The image to use as the basis for the variation(s). Must be a valid PNG file, less than 4MB, + * and square. + */ + fun image(): ByteArray = body.image() + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(): Optional = body.model() + + /** + * The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is + * supported. + */ + fun n(): Optional = body.n() + + /** + * The format in which the generated images are returned. Must be one of `url` or `b64_json`. + * URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(): Optional = body.responseFormat() + + /** The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. */ + fun size(): Optional = body.size() + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and detect + * abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun user(): Optional = body.user() + + /** + * The image to use as the basis for the variation(s). Must be a valid PNG file, less than 4MB, + * and square. + */ + fun _image(): MultipartField = body._image() + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun _model(): MultipartField = body._model() + + /** + * The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is + * supported. + */ + fun _n(): MultipartField = body._n() + + /** + * The format in which the generated images are returned. Must be one of `url` or `b64_json`. + * URLs are only valid for 60 minutes after the image has been generated. + */ + fun _responseFormat(): MultipartField = body._responseFormat() + + /** The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. */ + fun _size(): MultipartField = body._size() + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and detect + * abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun _user(): MultipartField = body._user() + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + @JvmSynthetic + internal fun _body(): Map> = + mapOf( + "image" to _image(), + "model" to _model(), + "n" to _n(), + "response_format" to _responseFormat(), + "size" to _size(), + "user" to _user(), + ) + .toImmutable() + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + @NoAutoDetect + class Body + @JsonCreator + private constructor( + private val image: MultipartField, + private val model: MultipartField, + private val n: MultipartField, + private val responseFormat: MultipartField, + private val size: MultipartField, + private val user: MultipartField, + ) { + + /** + * The image to use as the basis for the variation(s). Must be a valid PNG file, less than + * 4MB, and square. + */ + fun image(): ByteArray = image.value.getRequired("image") + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(): Optional = Optional.ofNullable(model.value.getNullable("model")) + + /** + * The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is + * supported. + */ + fun n(): Optional = Optional.ofNullable(n.value.getNullable("n")) + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(): Optional = + Optional.ofNullable(responseFormat.value.getNullable("response_format")) + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. + */ + fun size(): Optional = Optional.ofNullable(size.value.getNullable("size")) + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and + * detect abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun user(): Optional = Optional.ofNullable(user.value.getNullable("user")) + + /** + * The image to use as the basis for the variation(s). Must be a valid PNG file, less than + * 4MB, and square. + */ + fun _image(): MultipartField = image + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun _model(): MultipartField = model + + /** + * The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is + * supported. + */ + fun _n(): MultipartField = n + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun _responseFormat(): MultipartField = responseFormat + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. + */ + fun _size(): MultipartField = size + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and + * detect abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun _user(): MultipartField = user + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + image() + model() + n() + responseFormat() + size() + user() + validated = true + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var image: MultipartField? = null + private var model: MultipartField = MultipartField.of(null) + private var n: MultipartField = MultipartField.of(null) + private var responseFormat: MultipartField = MultipartField.of(null) + private var size: MultipartField = MultipartField.of(null) + private var user: MultipartField = MultipartField.of(null) + + @JvmSynthetic + internal fun from(body: Body) = apply { + image = body.image + model = body.model + n = body.n + responseFormat = body.responseFormat + size = body.size + user = body.user + } + + /** + * The image to use as the basis for the variation(s). Must be a valid PNG file, less + * than 4MB, and square. + */ + fun image(image: ByteArray) = image(MultipartField.of(image)) + + /** + * The image to use as the basis for the variation(s). Must be a valid PNG file, less + * than 4MB, and square. + */ + fun image(image: MultipartField) = apply { this.image = image } + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(model: ImageModel?) = model(MultipartField.of(model)) + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(model: Optional) = model(model.orElse(null)) + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(model: MultipartField) = apply { this.model = model } + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(value: String) = model(ImageModel.of(value)) + + /** + * The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + * `n=1` is supported. + */ + fun n(n: Long?) = n(MultipartField.of(n)) + + /** + * The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + * `n=1` is supported. + */ + fun n(n: Long) = n(n as Long?) + + /** + * The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + * `n=1` is supported. + */ + @Suppress("USELESS_CAST") // See https://youtrack.jetbrains.com/issue/KT-74228 + fun n(n: Optional) = n(n.orElse(null) as Long?) + + /** + * The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + * `n=1` is supported. + */ + fun n(n: MultipartField) = apply { this.n = n } + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(responseFormat: ResponseFormat?) = + responseFormat(MultipartField.of(responseFormat)) + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(responseFormat: Optional) = + responseFormat(responseFormat.orElse(null)) + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(responseFormat: MultipartField) = apply { + this.responseFormat = responseFormat + } + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or + * `1024x1024`. + */ + fun size(size: Size?) = size(MultipartField.of(size)) + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or + * `1024x1024`. + */ + fun size(size: Optional) = size(size.orElse(null)) + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or + * `1024x1024`. + */ + fun size(size: MultipartField) = apply { this.size = size } + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and + * detect abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun user(user: String) = user(MultipartField.of(user)) + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and + * detect abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun user(user: MultipartField) = apply { this.user = user } + + fun build(): Body = + Body(checkRequired("image", image), model, n, responseFormat, size, user) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && image == other.image && model == other.model && n == other.n && responseFormat == other.responseFormat && size == other.size && user == other.user /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(image, model, n, responseFormat, size, user) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{image=$image, model=$model, n=$n, responseFormat=$responseFormat, size=$size, user=$user}" + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ImageCreateVariationParams]. */ + @NoAutoDetect + class Builder internal constructor() { + + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(imageCreateVariationParams: ImageCreateVariationParams) = apply { + body = imageCreateVariationParams.body.toBuilder() + additionalHeaders = imageCreateVariationParams.additionalHeaders.toBuilder() + additionalQueryParams = imageCreateVariationParams.additionalQueryParams.toBuilder() + } + + /** + * The image to use as the basis for the variation(s). Must be a valid PNG file, less than + * 4MB, and square. + */ + fun image(image: ByteArray) = apply { body.image(image) } + + /** + * The image to use as the basis for the variation(s). Must be a valid PNG file, less than + * 4MB, and square. + */ + fun image(image: MultipartField) = apply { body.image(image) } + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(model: ImageModel?) = apply { body.model(model) } + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(model: Optional) = model(model.orElse(null)) + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(model: MultipartField) = apply { body.model(model) } + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(value: String) = apply { body.model(value) } + + /** + * The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is + * supported. + */ + fun n(n: Long?) = apply { body.n(n) } + + /** + * The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is + * supported. + */ + fun n(n: Long) = n(n as Long?) + + /** + * The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is + * supported. + */ + @Suppress("USELESS_CAST") // See https://youtrack.jetbrains.com/issue/KT-74228 + fun n(n: Optional) = n(n.orElse(null) as Long?) + + /** + * The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is + * supported. + */ + fun n(n: MultipartField) = apply { body.n(n) } + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(responseFormat: ResponseFormat?) = apply { + body.responseFormat(responseFormat) + } + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(responseFormat: Optional) = + responseFormat(responseFormat.orElse(null)) + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(responseFormat: MultipartField) = apply { + body.responseFormat(responseFormat) + } + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. + */ + fun size(size: Size?) = apply { body.size(size) } + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. + */ + fun size(size: Optional) = size(size.orElse(null)) + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. + */ + fun size(size: MultipartField) = apply { body.size(size) } + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and + * detect abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun user(user: String) = apply { body.user(user) } + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and + * detect abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun user(user: MultipartField) = apply { body.user(user) } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + fun build(): ImageCreateVariationParams = + ImageCreateVariationParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + /** + * The format in which the generated images are returned. Must be one of `url` or `b64_json`. + * URLs are only valid for 60 minutes after the image has been generated. + */ + class ResponseFormat @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val URL = of("url") + + @JvmField val B64_JSON = of("b64_json") + + @JvmStatic fun of(value: String) = ResponseFormat(JsonField.of(value)) + } + + /** An enum containing [ResponseFormat]'s known values. */ + enum class Known { + URL, + B64_JSON, + } + + /** + * An enum containing [ResponseFormat]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [ResponseFormat] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + URL, + B64_JSON, + /** + * An enum member indicating that [ResponseFormat] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + URL -> Value.URL + B64_JSON -> Value.B64_JSON + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + URL -> Known.URL + B64_JSON -> Known.B64_JSON + else -> throw OpenAIInvalidDataException("Unknown ResponseFormat: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is ResponseFormat && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. */ + class Size @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val _256X256 = of("256x256") + + @JvmField val _512X512 = of("512x512") + + @JvmField val _1024X1024 = of("1024x1024") + + @JvmStatic fun of(value: String) = Size(JsonField.of(value)) + } + + /** An enum containing [Size]'s known values. */ + enum class Known { + _256X256, + _512X512, + _1024X1024, + } + + /** + * An enum containing [Size]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Size] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + _256X256, + _512X512, + _1024X1024, + /** An enum member indicating that [Size] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + _256X256 -> Value._256X256 + _512X512 -> Value._512X512 + _1024X1024 -> Value._1024X1024 + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + _256X256 -> Known._256X256 + _512X512 -> Known._512X512 + _1024X1024 -> Known._1024X1024 + else -> throw OpenAIInvalidDataException("Unknown Size: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Size && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is ImageCreateVariationParams && body == other.body && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(body, additionalHeaders, additionalQueryParams) /* spotless:on */ + + override fun toString() = + "ImageCreateVariationParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ImageEditParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/ImageEditParams.kt new file mode 100644 index 000000000..1fb4a52aa --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/ImageEditParams.kt @@ -0,0 +1,871 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonCreator +import com.openai.core.Enum +import com.openai.core.JsonField +import com.openai.core.MultipartField +import com.openai.core.NoAutoDetect +import com.openai.core.Params +import com.openai.core.checkRequired +import com.openai.core.http.Headers +import com.openai.core.http.QueryParams +import com.openai.core.toImmutable +import com.openai.errors.OpenAIInvalidDataException +import java.util.Objects +import java.util.Optional + +/** Creates an edited or extended image given an original image and a prompt. */ +class ImageEditParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * The image to edit. Must be a valid PNG file, less than 4MB, and square. If mask is not + * provided, image must have transparency, which will be used as the mask. + */ + fun image(): ByteArray = body.image() + + /** A text description of the desired image(s). The maximum length is 1000 characters. */ + fun prompt(): String = body.prompt() + + /** + * An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where + * `image` should be edited. Must be a valid PNG file, less than 4MB, and have the same + * dimensions as `image`. + */ + fun mask(): Optional = body.mask() + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(): Optional = body.model() + + /** The number of images to generate. Must be between 1 and 10. */ + fun n(): Optional = body.n() + + /** + * The format in which the generated images are returned. Must be one of `url` or `b64_json`. + * URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(): Optional = body.responseFormat() + + /** The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. */ + fun size(): Optional = body.size() + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and detect + * abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun user(): Optional = body.user() + + /** + * The image to edit. Must be a valid PNG file, less than 4MB, and square. If mask is not + * provided, image must have transparency, which will be used as the mask. + */ + fun _image(): MultipartField = body._image() + + /** A text description of the desired image(s). The maximum length is 1000 characters. */ + fun _prompt(): MultipartField = body._prompt() + + /** + * An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where + * `image` should be edited. Must be a valid PNG file, less than 4MB, and have the same + * dimensions as `image`. + */ + fun _mask(): MultipartField = body._mask() + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun _model(): MultipartField = body._model() + + /** The number of images to generate. Must be between 1 and 10. */ + fun _n(): MultipartField = body._n() + + /** + * The format in which the generated images are returned. Must be one of `url` or `b64_json`. + * URLs are only valid for 60 minutes after the image has been generated. + */ + fun _responseFormat(): MultipartField = body._responseFormat() + + /** The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. */ + fun _size(): MultipartField = body._size() + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and detect + * abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun _user(): MultipartField = body._user() + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + @JvmSynthetic + internal fun _body(): Map> = + mapOf( + "image" to _image(), + "prompt" to _prompt(), + "mask" to _mask(), + "model" to _model(), + "n" to _n(), + "response_format" to _responseFormat(), + "size" to _size(), + "user" to _user(), + ) + .toImmutable() + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + @NoAutoDetect + class Body + @JsonCreator + private constructor( + private val image: MultipartField, + private val prompt: MultipartField, + private val mask: MultipartField, + private val model: MultipartField, + private val n: MultipartField, + private val responseFormat: MultipartField, + private val size: MultipartField, + private val user: MultipartField, + ) { + + /** + * The image to edit. Must be a valid PNG file, less than 4MB, and square. If mask is not + * provided, image must have transparency, which will be used as the mask. + */ + fun image(): ByteArray = image.value.getRequired("image") + + /** A text description of the desired image(s). The maximum length is 1000 characters. */ + fun prompt(): String = prompt.value.getRequired("prompt") + + /** + * An additional image whose fully transparent areas (e.g. where alpha is zero) indicate + * where `image` should be edited. Must be a valid PNG file, less than 4MB, and have the + * same dimensions as `image`. + */ + fun mask(): Optional = Optional.ofNullable(mask.value.getNullable("mask")) + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(): Optional = Optional.ofNullable(model.value.getNullable("model")) + + /** The number of images to generate. Must be between 1 and 10. */ + fun n(): Optional = Optional.ofNullable(n.value.getNullable("n")) + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(): Optional = + Optional.ofNullable(responseFormat.value.getNullable("response_format")) + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. + */ + fun size(): Optional = Optional.ofNullable(size.value.getNullable("size")) + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and + * detect abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun user(): Optional = Optional.ofNullable(user.value.getNullable("user")) + + /** + * The image to edit. Must be a valid PNG file, less than 4MB, and square. If mask is not + * provided, image must have transparency, which will be used as the mask. + */ + fun _image(): MultipartField = image + + /** A text description of the desired image(s). The maximum length is 1000 characters. */ + fun _prompt(): MultipartField = prompt + + /** + * An additional image whose fully transparent areas (e.g. where alpha is zero) indicate + * where `image` should be edited. Must be a valid PNG file, less than 4MB, and have the + * same dimensions as `image`. + */ + fun _mask(): MultipartField = mask + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun _model(): MultipartField = model + + /** The number of images to generate. Must be between 1 and 10. */ + fun _n(): MultipartField = n + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun _responseFormat(): MultipartField = responseFormat + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. + */ + fun _size(): MultipartField = size + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and + * detect abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun _user(): MultipartField = user + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + image() + prompt() + mask() + model() + n() + responseFormat() + size() + user() + validated = true + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var image: MultipartField? = null + private var prompt: MultipartField? = null + private var mask: MultipartField = MultipartField.of(null) + private var model: MultipartField = MultipartField.of(null) + private var n: MultipartField = MultipartField.of(null) + private var responseFormat: MultipartField = MultipartField.of(null) + private var size: MultipartField = MultipartField.of(null) + private var user: MultipartField = MultipartField.of(null) + + @JvmSynthetic + internal fun from(body: Body) = apply { + image = body.image + prompt = body.prompt + mask = body.mask + model = body.model + n = body.n + responseFormat = body.responseFormat + size = body.size + user = body.user + } + + /** + * The image to edit. Must be a valid PNG file, less than 4MB, and square. If mask is + * not provided, image must have transparency, which will be used as the mask. + */ + fun image(image: ByteArray) = image(MultipartField.of(image)) + + /** + * The image to edit. Must be a valid PNG file, less than 4MB, and square. If mask is + * not provided, image must have transparency, which will be used as the mask. + */ + fun image(image: MultipartField) = apply { this.image = image } + + /** + * A text description of the desired image(s). The maximum length is 1000 characters. + */ + fun prompt(prompt: String) = prompt(MultipartField.of(prompt)) + + /** + * A text description of the desired image(s). The maximum length is 1000 characters. + */ + fun prompt(prompt: MultipartField) = apply { this.prompt = prompt } + + /** + * An additional image whose fully transparent areas (e.g. where alpha is zero) indicate + * where `image` should be edited. Must be a valid PNG file, less than 4MB, and have the + * same dimensions as `image`. + */ + fun mask(mask: ByteArray) = mask(MultipartField.of(mask)) + + /** + * An additional image whose fully transparent areas (e.g. where alpha is zero) indicate + * where `image` should be edited. Must be a valid PNG file, less than 4MB, and have the + * same dimensions as `image`. + */ + fun mask(mask: MultipartField) = apply { this.mask = mask } + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(model: ImageModel?) = model(MultipartField.of(model)) + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(model: Optional) = model(model.orElse(null)) + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(model: MultipartField) = apply { this.model = model } + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(value: String) = model(ImageModel.of(value)) + + /** The number of images to generate. Must be between 1 and 10. */ + fun n(n: Long?) = n(MultipartField.of(n)) + + /** The number of images to generate. Must be between 1 and 10. */ + fun n(n: Long) = n(n as Long?) + + /** The number of images to generate. Must be between 1 and 10. */ + @Suppress("USELESS_CAST") // See https://youtrack.jetbrains.com/issue/KT-74228 + fun n(n: Optional) = n(n.orElse(null) as Long?) + + /** The number of images to generate. Must be between 1 and 10. */ + fun n(n: MultipartField) = apply { this.n = n } + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(responseFormat: ResponseFormat?) = + responseFormat(MultipartField.of(responseFormat)) + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(responseFormat: Optional) = + responseFormat(responseFormat.orElse(null)) + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(responseFormat: MultipartField) = apply { + this.responseFormat = responseFormat + } + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or + * `1024x1024`. + */ + fun size(size: Size?) = size(MultipartField.of(size)) + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or + * `1024x1024`. + */ + fun size(size: Optional) = size(size.orElse(null)) + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or + * `1024x1024`. + */ + fun size(size: MultipartField) = apply { this.size = size } + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and + * detect abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun user(user: String) = user(MultipartField.of(user)) + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and + * detect abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun user(user: MultipartField) = apply { this.user = user } + + fun build(): Body = + Body( + checkRequired("image", image), + checkRequired("prompt", prompt), + mask, + model, + n, + responseFormat, + size, + user, + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && image == other.image && prompt == other.prompt && mask == other.mask && model == other.model && n == other.n && responseFormat == other.responseFormat && size == other.size && user == other.user /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(image, prompt, mask, model, n, responseFormat, size, user) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{image=$image, prompt=$prompt, mask=$mask, model=$model, n=$n, responseFormat=$responseFormat, size=$size, user=$user}" + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ImageEditParams]. */ + @NoAutoDetect + class Builder internal constructor() { + + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(imageEditParams: ImageEditParams) = apply { + body = imageEditParams.body.toBuilder() + additionalHeaders = imageEditParams.additionalHeaders.toBuilder() + additionalQueryParams = imageEditParams.additionalQueryParams.toBuilder() + } + + /** + * The image to edit. Must be a valid PNG file, less than 4MB, and square. If mask is not + * provided, image must have transparency, which will be used as the mask. + */ + fun image(image: ByteArray) = apply { body.image(image) } + + /** + * The image to edit. Must be a valid PNG file, less than 4MB, and square. If mask is not + * provided, image must have transparency, which will be used as the mask. + */ + fun image(image: MultipartField) = apply { body.image(image) } + + /** A text description of the desired image(s). The maximum length is 1000 characters. */ + fun prompt(prompt: String) = apply { body.prompt(prompt) } + + /** A text description of the desired image(s). The maximum length is 1000 characters. */ + fun prompt(prompt: MultipartField) = apply { body.prompt(prompt) } + + /** + * An additional image whose fully transparent areas (e.g. where alpha is zero) indicate + * where `image` should be edited. Must be a valid PNG file, less than 4MB, and have the + * same dimensions as `image`. + */ + fun mask(mask: ByteArray) = apply { body.mask(mask) } + + /** + * An additional image whose fully transparent areas (e.g. where alpha is zero) indicate + * where `image` should be edited. Must be a valid PNG file, less than 4MB, and have the + * same dimensions as `image`. + */ + fun mask(mask: MultipartField) = apply { body.mask(mask) } + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(model: ImageModel?) = apply { body.model(model) } + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(model: Optional) = model(model.orElse(null)) + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(model: MultipartField) = apply { body.model(model) } + + /** The model to use for image generation. Only `dall-e-2` is supported at this time. */ + fun model(value: String) = apply { body.model(value) } + + /** The number of images to generate. Must be between 1 and 10. */ + fun n(n: Long?) = apply { body.n(n) } + + /** The number of images to generate. Must be between 1 and 10. */ + fun n(n: Long) = n(n as Long?) + + /** The number of images to generate. Must be between 1 and 10. */ + @Suppress("USELESS_CAST") // See https://youtrack.jetbrains.com/issue/KT-74228 + fun n(n: Optional) = n(n.orElse(null) as Long?) + + /** The number of images to generate. Must be between 1 and 10. */ + fun n(n: MultipartField) = apply { body.n(n) } + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(responseFormat: ResponseFormat?) = apply { + body.responseFormat(responseFormat) + } + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(responseFormat: Optional) = + responseFormat(responseFormat.orElse(null)) + + /** + * The format in which the generated images are returned. Must be one of `url` or + * `b64_json`. URLs are only valid for 60 minutes after the image has been generated. + */ + fun responseFormat(responseFormat: MultipartField) = apply { + body.responseFormat(responseFormat) + } + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. + */ + fun size(size: Size?) = apply { body.size(size) } + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. + */ + fun size(size: Optional) = size(size.orElse(null)) + + /** + * The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. + */ + fun size(size: MultipartField) = apply { body.size(size) } + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and + * detect abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun user(user: String) = apply { body.user(user) } + + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor and + * detect abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + */ + fun user(user: MultipartField) = apply { body.user(user) } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + fun build(): ImageEditParams = + ImageEditParams(body.build(), additionalHeaders.build(), additionalQueryParams.build()) + } + + /** + * The format in which the generated images are returned. Must be one of `url` or `b64_json`. + * URLs are only valid for 60 minutes after the image has been generated. + */ + class ResponseFormat @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val URL = of("url") + + @JvmField val B64_JSON = of("b64_json") + + @JvmStatic fun of(value: String) = ResponseFormat(JsonField.of(value)) + } + + /** An enum containing [ResponseFormat]'s known values. */ + enum class Known { + URL, + B64_JSON, + } + + /** + * An enum containing [ResponseFormat]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [ResponseFormat] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + URL, + B64_JSON, + /** + * An enum member indicating that [ResponseFormat] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + URL -> Value.URL + B64_JSON -> Value.B64_JSON + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + URL -> Known.URL + B64_JSON -> Known.B64_JSON + else -> throw OpenAIInvalidDataException("Unknown ResponseFormat: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is ResponseFormat && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. */ + class Size @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val _256X256 = of("256x256") + + @JvmField val _512X512 = of("512x512") + + @JvmField val _1024X1024 = of("1024x1024") + + @JvmStatic fun of(value: String) = Size(JsonField.of(value)) + } + + /** An enum containing [Size]'s known values. */ + enum class Known { + _256X256, + _512X512, + _1024X1024, + } + + /** + * An enum containing [Size]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Size] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + _256X256, + _512X512, + _1024X1024, + /** An enum member indicating that [Size] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + _256X256 -> Value._256X256 + _512X512 -> Value._512X512 + _1024X1024 -> Value._1024X1024 + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + _256X256 -> Known._256X256 + _512X512 -> Known._512X512 + _1024X1024 -> Known._1024X1024 + else -> throw OpenAIInvalidDataException("Unknown Size: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Size && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is ImageEditParams && body == other.body && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(body, additionalHeaders, additionalQueryParams) /* spotless:on */ + + override fun toString() = + "ImageEditParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ImagesResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/ImagesResponse.kt index 7309aa9e7..b2157ce27 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ImagesResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ImagesResponse.kt @@ -11,6 +11,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -86,14 +87,8 @@ private constructor( fun addData(data: Image) = apply { this.data = - (this.data ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(data) + (this.data ?: JsonField.of(mutableListOf())).also { + checkKnown("data", it).add(data) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/Message.kt b/openai-java-core/src/main/kotlin/com/openai/models/Message.kt index 460d6ce34..e8073562d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/Message.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/Message.kt @@ -21,6 +21,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.immutableEmptyMap @@ -328,14 +329,8 @@ private constructor( /** A list of files attached to the message, and the tools they were added to. */ fun addAttachment(attachment: Attachment) = apply { attachments = - (attachments ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(attachment) + (attachments ?: JsonField.of(mutableListOf())).also { + checkKnown("attachments", it).add(attachment) } } @@ -364,14 +359,8 @@ private constructor( /** The content of the message in array of text and/or images. */ fun addContent(content: MessageContent) = apply { this.content = - (this.content ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(content) + (this.content ?: JsonField.of(mutableListOf())).also { + checkKnown("content", it).add(content) } } @@ -646,14 +635,8 @@ private constructor( /** The tools to add this file to. */ fun addTool(tool: Tool) = apply { tools = - (tools ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(tool) + (tools ?: JsonField.of(mutableListOf())).also { + checkKnown("tools", it).add(tool) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/MessageDelta.kt b/openai-java-core/src/main/kotlin/com/openai/models/MessageDelta.kt index 3d4fca7e1..9081f66eb 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/MessageDelta.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/MessageDelta.kt @@ -12,6 +12,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException @@ -93,14 +94,8 @@ private constructor( /** The content of the message in array of text and/or images. */ fun addContent(content: MessageContentDelta) = apply { this.content = - (this.content ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(content) + (this.content ?: JsonField.of(mutableListOf())).also { + checkKnown("content", it).add(content) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/Moderation.kt b/openai-java-core/src/main/kotlin/com/openai/models/Moderation.kt index b8b6f9301..223d4f8ef 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/Moderation.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/Moderation.kt @@ -12,6 +12,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -928,14 +929,8 @@ private constructor( /** The applied input type(s) for the category 'harassment'. */ fun addHarassment(harassment: Harassment) = apply { this.harassment = - (this.harassment ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(harassment) + (this.harassment ?: JsonField.of(mutableListOf())).also { + checkKnown("harassment", it).add(harassment) } } @@ -953,14 +948,8 @@ private constructor( /** The applied input type(s) for the category 'harassment/threatening'. */ fun addHarassmentThreatening(harassmentThreatening: HarassmentThreatening) = apply { this.harassmentThreatening = - (this.harassmentThreatening ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(harassmentThreatening) + (this.harassmentThreatening ?: JsonField.of(mutableListOf())).also { + checkKnown("harassmentThreatening", it).add(harassmentThreatening) } } @@ -975,14 +964,8 @@ private constructor( /** The applied input type(s) for the category 'hate'. */ fun addHate(hate: Hate) = apply { this.hate = - (this.hate ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(hate) + (this.hate ?: JsonField.of(mutableListOf())).also { + checkKnown("hate", it).add(hate) } } @@ -998,14 +981,8 @@ private constructor( /** The applied input type(s) for the category 'hate/threatening'. */ fun addHateThreatening(hateThreatening: HateThreatening) = apply { this.hateThreatening = - (this.hateThreatening ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(hateThreatening) + (this.hateThreatening ?: JsonField.of(mutableListOf())).also { + checkKnown("hateThreatening", it).add(hateThreatening) } } @@ -1020,14 +997,8 @@ private constructor( /** The applied input type(s) for the category 'illicit'. */ fun addIllicit(illicit: Illicit) = apply { this.illicit = - (this.illicit ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(illicit) + (this.illicit ?: JsonField.of(mutableListOf())).also { + checkKnown("illicit", it).add(illicit) } } @@ -1043,14 +1014,8 @@ private constructor( /** The applied input type(s) for the category 'illicit/violent'. */ fun addIllicitViolent(illicitViolent: IllicitViolent) = apply { this.illicitViolent = - (this.illicitViolent ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(illicitViolent) + (this.illicitViolent ?: JsonField.of(mutableListOf())).also { + checkKnown("illicitViolent", it).add(illicitViolent) } } @@ -1065,14 +1030,8 @@ private constructor( /** The applied input type(s) for the category 'self-harm'. */ fun addSelfHarm(selfHarm: SelfHarm) = apply { this.selfHarm = - (this.selfHarm ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(selfHarm) + (this.selfHarm ?: JsonField.of(mutableListOf())).also { + checkKnown("selfHarm", it).add(selfHarm) } } @@ -1089,14 +1048,8 @@ private constructor( /** The applied input type(s) for the category 'self-harm/instructions'. */ fun addSelfHarmInstruction(selfHarmInstruction: SelfHarmInstruction) = apply { selfHarmInstructions = - (selfHarmInstructions ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(selfHarmInstruction) + (selfHarmInstructions ?: JsonField.of(mutableListOf())).also { + checkKnown("selfHarmInstructions", it).add(selfHarmInstruction) } } @@ -1112,14 +1065,8 @@ private constructor( /** The applied input type(s) for the category 'self-harm/intent'. */ fun addSelfHarmIntent(selfHarmIntent: SelfHarmIntent) = apply { this.selfHarmIntent = - (this.selfHarmIntent ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(selfHarmIntent) + (this.selfHarmIntent ?: JsonField.of(mutableListOf())).also { + checkKnown("selfHarmIntent", it).add(selfHarmIntent) } } @@ -1134,14 +1081,8 @@ private constructor( /** The applied input type(s) for the category 'sexual'. */ fun addSexual(sexual: Sexual) = apply { this.sexual = - (this.sexual ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(sexual) + (this.sexual ?: JsonField.of(mutableListOf())).also { + checkKnown("sexual", it).add(sexual) } } @@ -1157,14 +1098,8 @@ private constructor( /** The applied input type(s) for the category 'sexual/minors'. */ fun addSexualMinor(sexualMinor: SexualMinor) = apply { sexualMinors = - (sexualMinors ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(sexualMinor) + (sexualMinors ?: JsonField.of(mutableListOf())).also { + checkKnown("sexualMinors", it).add(sexualMinor) } } @@ -1179,14 +1114,8 @@ private constructor( /** The applied input type(s) for the category 'violence'. */ fun addViolence(violence: Violence) = apply { this.violence = - (this.violence ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(violence) + (this.violence ?: JsonField.of(mutableListOf())).also { + checkKnown("violence", it).add(violence) } } @@ -1202,14 +1131,8 @@ private constructor( /** The applied input type(s) for the category 'violence/graphic'. */ fun addViolenceGraphic(violenceGraphic: ViolenceGraphic) = apply { this.violenceGraphic = - (this.violenceGraphic ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(violenceGraphic) + (this.violenceGraphic ?: JsonField.of(mutableListOf())).also { + checkKnown("violenceGraphic", it).add(violenceGraphic) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ModerationCreateResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/ModerationCreateResponse.kt index 2d361e07e..b8858cbbe 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ModerationCreateResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ModerationCreateResponse.kt @@ -11,6 +11,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -110,14 +111,8 @@ private constructor( /** A list of moderation objects. */ fun addResult(result: Moderation) = apply { results = - (results ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(result) + (results ?: JsonField.of(mutableListOf())).also { + checkKnown("results", it).add(result) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/Run.kt b/openai-java-core/src/main/kotlin/com/openai/models/Run.kt index 1abe8b4ea..312d1ba97 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/Run.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/Run.kt @@ -12,6 +12,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -1048,15 +1049,7 @@ private constructor( */ fun addTool(tool: AssistantTool) = apply { tools = - (tools ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(tool) - } + (tools ?: JsonField.of(mutableListOf())).also { checkKnown("tools", it).add(tool) } } /** @@ -1823,14 +1816,8 @@ private constructor( /** A list of the relevant tool calls. */ fun addToolCall(toolCall: RequiredActionFunctionToolCall) = apply { toolCalls = - (toolCalls ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(toolCall) + (toolCalls ?: JsonField.of(mutableListOf())).also { + checkKnown("toolCalls", it).add(toolCall) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/SpeechModel.kt b/openai-java-core/src/main/kotlin/com/openai/models/SpeechModel.kt new file mode 100644 index 000000000..77056a21f --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/SpeechModel.kt @@ -0,0 +1,104 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonCreator +import com.openai.core.Enum +import com.openai.core.JsonField +import com.openai.errors.OpenAIInvalidDataException + +class SpeechModel @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't match + * any known member, and you want to know that value. For example, if the SDK is on an older + * version than the API, then the API may respond with new members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val TTS_1 = of("tts-1") + + @JvmField val TTS_1_HD = of("tts-1-hd") + + @JvmStatic fun of(value: String) = SpeechModel(JsonField.of(value)) + } + + /** An enum containing [SpeechModel]'s known values. */ + enum class Known { + TTS_1, + TTS_1_HD, + } + + /** + * An enum containing [SpeechModel]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [SpeechModel] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the SDK + * is on an older version than the API, then the API may respond with new members that the SDK + * is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + TTS_1, + TTS_1_HD, + /** An enum member indicating that [SpeechModel] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] if + * the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want to + * throw for the unknown case. + */ + fun value(): Value = + when (this) { + TTS_1 -> Value.TTS_1 + TTS_1_HD -> Value.TTS_1_HD + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't want + * to throw for the unknown case. + * + * @throws OpenAIInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + TTS_1 -> Known.TTS_1 + TTS_1_HD -> Known.TTS_1_HD + else -> throw OpenAIInvalidDataException("Unknown SpeechModel: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging and + * generally doesn't throw. + * + * @throws OpenAIInvalidDataException if this class instance's value does not have the expected + * primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is SpeechModel && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/Text.kt b/openai-java-core/src/main/kotlin/com/openai/models/Text.kt index 0794ba9cc..27cdf243b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/Text.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/Text.kt @@ -11,6 +11,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -84,14 +85,8 @@ private constructor( fun addAnnotation(annotation: Annotation) = apply { annotations = - (annotations ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(annotation) + (annotations ?: JsonField.of(mutableListOf())).also { + checkKnown("annotations", it).add(annotation) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/TextDelta.kt b/openai-java-core/src/main/kotlin/com/openai/models/TextDelta.kt index 071207d2d..c75e569be 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/TextDelta.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/TextDelta.kt @@ -11,6 +11,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable import java.util.Objects @@ -85,14 +86,8 @@ private constructor( fun addAnnotation(annotation: AnnotationDelta) = apply { annotations = - (annotations ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(annotation) + (annotations ?: JsonField.of(mutableListOf())).also { + checkKnown("annotations", it).add(annotation) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/Thread.kt b/openai-java-core/src/main/kotlin/com/openai/models/Thread.kt index 200662bc7..e23d0ad26 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/Thread.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/Thread.kt @@ -11,6 +11,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -436,14 +437,8 @@ private constructor( */ fun addFileId(fileId: String) = apply { fileIds = - (fileIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(fileId) + (fileIds ?: JsonField.of(mutableListOf())).also { + checkKnown("fileIds", it).add(fileId) } } @@ -585,14 +580,8 @@ private constructor( */ fun addVectorStoreId(vectorStoreId: String) = apply { vectorStoreIds = - (vectorStoreIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(vectorStoreId) + (vectorStoreIds ?: JsonField.of(mutableListOf())).also { + checkKnown("vectorStoreIds", it).add(vectorStoreId) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ToolCallDeltaObject.kt b/openai-java-core/src/main/kotlin/com/openai/models/ToolCallDeltaObject.kt index 6d00d3fec..c0e57625c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ToolCallDeltaObject.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ToolCallDeltaObject.kt @@ -11,6 +11,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException @@ -111,14 +112,8 @@ private constructor( */ fun addToolCall(toolCall: ToolCallDelta) = apply { toolCalls = - (toolCalls ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(toolCall) + (toolCalls ?: JsonField.of(mutableListOf())).also { + checkKnown("toolCalls", it).add(toolCall) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ToolCallsStepDetails.kt b/openai-java-core/src/main/kotlin/com/openai/models/ToolCallsStepDetails.kt index 3a0baed6c..474f90d86 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ToolCallsStepDetails.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ToolCallsStepDetails.kt @@ -11,6 +11,7 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.immutableEmptyMap import com.openai.core.toImmutable @@ -107,14 +108,8 @@ private constructor( */ fun addToolCall(toolCall: ToolCall) = apply { toolCalls = - (toolCalls ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(toolCall) + (toolCalls ?: JsonField.of(mutableListOf())).also { + checkKnown("toolCalls", it).add(toolCall) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/Transcription.kt b/openai-java-core/src/main/kotlin/com/openai/models/Transcription.kt new file mode 100644 index 000000000..8ffc75315 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/Transcription.kt @@ -0,0 +1,113 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.openai.core.ExcludeMissing +import com.openai.core.JsonField +import com.openai.core.JsonMissing +import com.openai.core.JsonValue +import com.openai.core.NoAutoDetect +import com.openai.core.checkRequired +import com.openai.core.immutableEmptyMap +import com.openai.core.toImmutable +import java.util.Objects + +/** Represents a transcription response returned by model, based on the provided input. */ +@NoAutoDetect +class Transcription +@JsonCreator +private constructor( + @JsonProperty("text") @ExcludeMissing private val text: JsonField = JsonMissing.of(), + @JsonAnySetter private val additionalProperties: Map = immutableEmptyMap(), +) { + + /** The transcribed text. */ + fun text(): String = text.getRequired("text") + + /** The transcribed text. */ + @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + private var validated: Boolean = false + + fun validate(): Transcription = apply { + if (validated) { + return@apply + } + + text() + validated = true + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Transcription]. */ + class Builder internal constructor() { + + private var text: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(transcription: Transcription) = apply { + text = transcription.text + additionalProperties = transcription.additionalProperties.toMutableMap() + } + + /** The transcribed text. */ + fun text(text: String) = text(JsonField.of(text)) + + /** The transcribed text. */ + fun text(text: JsonField) = apply { this.text = text } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + fun build(): Transcription = + Transcription(checkRequired("text", text), additionalProperties.toImmutable()) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Transcription && text == other.text && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(text, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Transcription{text=$text, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/TranscriptionSegment.kt b/openai-java-core/src/main/kotlin/com/openai/models/TranscriptionSegment.kt new file mode 100644 index 000000000..107ab56e6 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/TranscriptionSegment.kt @@ -0,0 +1,332 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.openai.core.ExcludeMissing +import com.openai.core.JsonField +import com.openai.core.JsonMissing +import com.openai.core.JsonValue +import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown +import com.openai.core.checkRequired +import com.openai.core.immutableEmptyMap +import com.openai.core.toImmutable +import java.util.Objects + +@NoAutoDetect +class TranscriptionSegment +@JsonCreator +private constructor( + @JsonProperty("id") @ExcludeMissing private val id: JsonField = JsonMissing.of(), + @JsonProperty("avg_logprob") + @ExcludeMissing + private val avgLogprob: JsonField = JsonMissing.of(), + @JsonProperty("compression_ratio") + @ExcludeMissing + private val compressionRatio: JsonField = JsonMissing.of(), + @JsonProperty("end") @ExcludeMissing private val end: JsonField = JsonMissing.of(), + @JsonProperty("no_speech_prob") + @ExcludeMissing + private val noSpeechProb: JsonField = JsonMissing.of(), + @JsonProperty("seek") @ExcludeMissing private val seek: JsonField = JsonMissing.of(), + @JsonProperty("start") @ExcludeMissing private val start: JsonField = JsonMissing.of(), + @JsonProperty("temperature") + @ExcludeMissing + private val temperature: JsonField = JsonMissing.of(), + @JsonProperty("text") @ExcludeMissing private val text: JsonField = JsonMissing.of(), + @JsonProperty("tokens") + @ExcludeMissing + private val tokens: JsonField> = JsonMissing.of(), + @JsonAnySetter private val additionalProperties: Map = immutableEmptyMap(), +) { + + /** Unique identifier of the segment. */ + fun id(): Long = id.getRequired("id") + + /** + * Average logprob of the segment. If the value is lower than -1, consider the logprobs failed. + */ + fun avgLogprob(): Double = avgLogprob.getRequired("avg_logprob") + + /** + * Compression ratio of the segment. If the value is greater than 2.4, consider the compression + * failed. + */ + fun compressionRatio(): Double = compressionRatio.getRequired("compression_ratio") + + /** End time of the segment in seconds. */ + fun end(): Double = end.getRequired("end") + + /** + * Probability of no speech in the segment. If the value is higher than 1.0 and the + * `avg_logprob` is below -1, consider this segment silent. + */ + fun noSpeechProb(): Double = noSpeechProb.getRequired("no_speech_prob") + + /** Seek offset of the segment. */ + fun seek(): Long = seek.getRequired("seek") + + /** Start time of the segment in seconds. */ + fun start(): Double = start.getRequired("start") + + /** Temperature parameter used for generating the segment. */ + fun temperature(): Double = temperature.getRequired("temperature") + + /** Text content of the segment. */ + fun text(): String = text.getRequired("text") + + /** Array of token IDs for the text content. */ + fun tokens(): List = tokens.getRequired("tokens") + + /** Unique identifier of the segment. */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Average logprob of the segment. If the value is lower than -1, consider the logprobs failed. + */ + @JsonProperty("avg_logprob") @ExcludeMissing fun _avgLogprob(): JsonField = avgLogprob + + /** + * Compression ratio of the segment. If the value is greater than 2.4, consider the compression + * failed. + */ + @JsonProperty("compression_ratio") + @ExcludeMissing + fun _compressionRatio(): JsonField = compressionRatio + + /** End time of the segment in seconds. */ + @JsonProperty("end") @ExcludeMissing fun _end(): JsonField = end + + /** + * Probability of no speech in the segment. If the value is higher than 1.0 and the + * `avg_logprob` is below -1, consider this segment silent. + */ + @JsonProperty("no_speech_prob") + @ExcludeMissing + fun _noSpeechProb(): JsonField = noSpeechProb + + /** Seek offset of the segment. */ + @JsonProperty("seek") @ExcludeMissing fun _seek(): JsonField = seek + + /** Start time of the segment in seconds. */ + @JsonProperty("start") @ExcludeMissing fun _start(): JsonField = start + + /** Temperature parameter used for generating the segment. */ + @JsonProperty("temperature") @ExcludeMissing fun _temperature(): JsonField = temperature + + /** Text content of the segment. */ + @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text + + /** Array of token IDs for the text content. */ + @JsonProperty("tokens") @ExcludeMissing fun _tokens(): JsonField> = tokens + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + private var validated: Boolean = false + + fun validate(): TranscriptionSegment = apply { + if (validated) { + return@apply + } + + id() + avgLogprob() + compressionRatio() + end() + noSpeechProb() + seek() + start() + temperature() + text() + tokens() + validated = true + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [TranscriptionSegment]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var avgLogprob: JsonField? = null + private var compressionRatio: JsonField? = null + private var end: JsonField? = null + private var noSpeechProb: JsonField? = null + private var seek: JsonField? = null + private var start: JsonField? = null + private var temperature: JsonField? = null + private var text: JsonField? = null + private var tokens: JsonField>? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(transcriptionSegment: TranscriptionSegment) = apply { + id = transcriptionSegment.id + avgLogprob = transcriptionSegment.avgLogprob + compressionRatio = transcriptionSegment.compressionRatio + end = transcriptionSegment.end + noSpeechProb = transcriptionSegment.noSpeechProb + seek = transcriptionSegment.seek + start = transcriptionSegment.start + temperature = transcriptionSegment.temperature + text = transcriptionSegment.text + tokens = transcriptionSegment.tokens.map { it.toMutableList() } + additionalProperties = transcriptionSegment.additionalProperties.toMutableMap() + } + + /** Unique identifier of the segment. */ + fun id(id: Long) = id(JsonField.of(id)) + + /** Unique identifier of the segment. */ + fun id(id: JsonField) = apply { this.id = id } + + /** + * Average logprob of the segment. If the value is lower than -1, consider the logprobs + * failed. + */ + fun avgLogprob(avgLogprob: Double) = avgLogprob(JsonField.of(avgLogprob)) + + /** + * Average logprob of the segment. If the value is lower than -1, consider the logprobs + * failed. + */ + fun avgLogprob(avgLogprob: JsonField) = apply { this.avgLogprob = avgLogprob } + + /** + * Compression ratio of the segment. If the value is greater than 2.4, consider the + * compression failed. + */ + fun compressionRatio(compressionRatio: Double) = + compressionRatio(JsonField.of(compressionRatio)) + + /** + * Compression ratio of the segment. If the value is greater than 2.4, consider the + * compression failed. + */ + fun compressionRatio(compressionRatio: JsonField) = apply { + this.compressionRatio = compressionRatio + } + + /** End time of the segment in seconds. */ + fun end(end: Double) = end(JsonField.of(end)) + + /** End time of the segment in seconds. */ + fun end(end: JsonField) = apply { this.end = end } + + /** + * Probability of no speech in the segment. If the value is higher than 1.0 and the + * `avg_logprob` is below -1, consider this segment silent. + */ + fun noSpeechProb(noSpeechProb: Double) = noSpeechProb(JsonField.of(noSpeechProb)) + + /** + * Probability of no speech in the segment. If the value is higher than 1.0 and the + * `avg_logprob` is below -1, consider this segment silent. + */ + fun noSpeechProb(noSpeechProb: JsonField) = apply { + this.noSpeechProb = noSpeechProb + } + + /** Seek offset of the segment. */ + fun seek(seek: Long) = seek(JsonField.of(seek)) + + /** Seek offset of the segment. */ + fun seek(seek: JsonField) = apply { this.seek = seek } + + /** Start time of the segment in seconds. */ + fun start(start: Double) = start(JsonField.of(start)) + + /** Start time of the segment in seconds. */ + fun start(start: JsonField) = apply { this.start = start } + + /** Temperature parameter used for generating the segment. */ + fun temperature(temperature: Double) = temperature(JsonField.of(temperature)) + + /** Temperature parameter used for generating the segment. */ + fun temperature(temperature: JsonField) = apply { this.temperature = temperature } + + /** Text content of the segment. */ + fun text(text: String) = text(JsonField.of(text)) + + /** Text content of the segment. */ + fun text(text: JsonField) = apply { this.text = text } + + /** Array of token IDs for the text content. */ + fun tokens(tokens: List) = tokens(JsonField.of(tokens)) + + /** Array of token IDs for the text content. */ + fun tokens(tokens: JsonField>) = apply { + this.tokens = tokens.map { it.toMutableList() } + } + + /** Array of token IDs for the text content. */ + fun addToken(token: Long) = apply { + tokens = + (tokens ?: JsonField.of(mutableListOf())).also { + checkKnown("tokens", it).add(token) + } + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + fun build(): TranscriptionSegment = + TranscriptionSegment( + checkRequired("id", id), + checkRequired("avgLogprob", avgLogprob), + checkRequired("compressionRatio", compressionRatio), + checkRequired("end", end), + checkRequired("noSpeechProb", noSpeechProb), + checkRequired("seek", seek), + checkRequired("start", start), + checkRequired("temperature", temperature), + checkRequired("text", text), + checkRequired("tokens", tokens).map { it.toImmutable() }, + additionalProperties.toImmutable(), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is TranscriptionSegment && id == other.id && avgLogprob == other.avgLogprob && compressionRatio == other.compressionRatio && end == other.end && noSpeechProb == other.noSpeechProb && seek == other.seek && start == other.start && temperature == other.temperature && text == other.text && tokens == other.tokens && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(id, avgLogprob, compressionRatio, end, noSpeechProb, seek, start, temperature, text, tokens, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "TranscriptionSegment{id=$id, avgLogprob=$avgLogprob, compressionRatio=$compressionRatio, end=$end, noSpeechProb=$noSpeechProb, seek=$seek, start=$start, temperature=$temperature, text=$text, tokens=$tokens, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/TranscriptionVerbose.kt b/openai-java-core/src/main/kotlin/com/openai/models/TranscriptionVerbose.kt new file mode 100644 index 000000000..7259f943a --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/TranscriptionVerbose.kt @@ -0,0 +1,217 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.openai.core.ExcludeMissing +import com.openai.core.JsonField +import com.openai.core.JsonMissing +import com.openai.core.JsonValue +import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown +import com.openai.core.checkRequired +import com.openai.core.immutableEmptyMap +import com.openai.core.toImmutable +import java.util.Objects +import java.util.Optional + +/** + * Represents a verbose json transcription response returned by model, based on the provided input. + */ +@NoAutoDetect +class TranscriptionVerbose +@JsonCreator +private constructor( + @JsonProperty("duration") + @ExcludeMissing + private val duration: JsonField = JsonMissing.of(), + @JsonProperty("language") + @ExcludeMissing + private val language: JsonField = JsonMissing.of(), + @JsonProperty("text") @ExcludeMissing private val text: JsonField = JsonMissing.of(), + @JsonProperty("segments") + @ExcludeMissing + private val segments: JsonField> = JsonMissing.of(), + @JsonProperty("words") + @ExcludeMissing + private val words: JsonField> = JsonMissing.of(), + @JsonAnySetter private val additionalProperties: Map = immutableEmptyMap(), +) { + + /** The duration of the input audio. */ + fun duration(): Double = duration.getRequired("duration") + + /** The language of the input audio. */ + fun language(): String = language.getRequired("language") + + /** The transcribed text. */ + fun text(): String = text.getRequired("text") + + /** Segments of the transcribed text and their corresponding details. */ + fun segments(): Optional> = + Optional.ofNullable(segments.getNullable("segments")) + + /** Extracted words and their corresponding timestamps. */ + fun words(): Optional> = Optional.ofNullable(words.getNullable("words")) + + /** The duration of the input audio. */ + @JsonProperty("duration") @ExcludeMissing fun _duration(): JsonField = duration + + /** The language of the input audio. */ + @JsonProperty("language") @ExcludeMissing fun _language(): JsonField = language + + /** The transcribed text. */ + @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text + + /** Segments of the transcribed text and their corresponding details. */ + @JsonProperty("segments") + @ExcludeMissing + fun _segments(): JsonField> = segments + + /** Extracted words and their corresponding timestamps. */ + @JsonProperty("words") @ExcludeMissing fun _words(): JsonField> = words + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + private var validated: Boolean = false + + fun validate(): TranscriptionVerbose = apply { + if (validated) { + return@apply + } + + duration() + language() + text() + segments().ifPresent { it.forEach { it.validate() } } + words().ifPresent { it.forEach { it.validate() } } + validated = true + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [TranscriptionVerbose]. */ + class Builder internal constructor() { + + private var duration: JsonField? = null + private var language: JsonField? = null + private var text: JsonField? = null + private var segments: JsonField>? = null + private var words: JsonField>? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(transcriptionVerbose: TranscriptionVerbose) = apply { + duration = transcriptionVerbose.duration + language = transcriptionVerbose.language + text = transcriptionVerbose.text + segments = transcriptionVerbose.segments.map { it.toMutableList() } + words = transcriptionVerbose.words.map { it.toMutableList() } + additionalProperties = transcriptionVerbose.additionalProperties.toMutableMap() + } + + /** The duration of the input audio. */ + fun duration(duration: Double) = duration(JsonField.of(duration)) + + /** The duration of the input audio. */ + fun duration(duration: JsonField) = apply { this.duration = duration } + + /** The language of the input audio. */ + fun language(language: String) = language(JsonField.of(language)) + + /** The language of the input audio. */ + fun language(language: JsonField) = apply { this.language = language } + + /** The transcribed text. */ + fun text(text: String) = text(JsonField.of(text)) + + /** The transcribed text. */ + fun text(text: JsonField) = apply { this.text = text } + + /** Segments of the transcribed text and their corresponding details. */ + fun segments(segments: List) = segments(JsonField.of(segments)) + + /** Segments of the transcribed text and their corresponding details. */ + fun segments(segments: JsonField>) = apply { + this.segments = segments.map { it.toMutableList() } + } + + /** Segments of the transcribed text and their corresponding details. */ + fun addSegment(segment: TranscriptionSegment) = apply { + segments = + (segments ?: JsonField.of(mutableListOf())).also { + checkKnown("segments", it).add(segment) + } + } + + /** Extracted words and their corresponding timestamps. */ + fun words(words: List) = words(JsonField.of(words)) + + /** Extracted words and their corresponding timestamps. */ + fun words(words: JsonField>) = apply { + this.words = words.map { it.toMutableList() } + } + + /** Extracted words and their corresponding timestamps. */ + fun addWord(word: TranscriptionWord) = apply { + words = + (words ?: JsonField.of(mutableListOf())).also { checkKnown("words", it).add(word) } + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + fun build(): TranscriptionVerbose = + TranscriptionVerbose( + checkRequired("duration", duration), + checkRequired("language", language), + checkRequired("text", text), + (segments ?: JsonMissing.of()).map { it.toImmutable() }, + (words ?: JsonMissing.of()).map { it.toImmutable() }, + additionalProperties.toImmutable(), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is TranscriptionVerbose && duration == other.duration && language == other.language && text == other.text && segments == other.segments && words == other.words && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(duration, language, text, segments, words, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "TranscriptionVerbose{duration=$duration, language=$language, text=$text, segments=$segments, words=$words, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/TranscriptionWord.kt b/openai-java-core/src/main/kotlin/com/openai/models/TranscriptionWord.kt new file mode 100644 index 000000000..b0f15b38c --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/TranscriptionWord.kt @@ -0,0 +1,149 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.openai.core.ExcludeMissing +import com.openai.core.JsonField +import com.openai.core.JsonMissing +import com.openai.core.JsonValue +import com.openai.core.NoAutoDetect +import com.openai.core.checkRequired +import com.openai.core.immutableEmptyMap +import com.openai.core.toImmutable +import java.util.Objects + +@NoAutoDetect +class TranscriptionWord +@JsonCreator +private constructor( + @JsonProperty("end") @ExcludeMissing private val end: JsonField = JsonMissing.of(), + @JsonProperty("start") @ExcludeMissing private val start: JsonField = JsonMissing.of(), + @JsonProperty("word") @ExcludeMissing private val word: JsonField = JsonMissing.of(), + @JsonAnySetter private val additionalProperties: Map = immutableEmptyMap(), +) { + + /** End time of the word in seconds. */ + fun end(): Double = end.getRequired("end") + + /** Start time of the word in seconds. */ + fun start(): Double = start.getRequired("start") + + /** The text content of the word. */ + fun word(): String = word.getRequired("word") + + /** End time of the word in seconds. */ + @JsonProperty("end") @ExcludeMissing fun _end(): JsonField = end + + /** Start time of the word in seconds. */ + @JsonProperty("start") @ExcludeMissing fun _start(): JsonField = start + + /** The text content of the word. */ + @JsonProperty("word") @ExcludeMissing fun _word(): JsonField = word + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + private var validated: Boolean = false + + fun validate(): TranscriptionWord = apply { + if (validated) { + return@apply + } + + end() + start() + word() + validated = true + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [TranscriptionWord]. */ + class Builder internal constructor() { + + private var end: JsonField? = null + private var start: JsonField? = null + private var word: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(transcriptionWord: TranscriptionWord) = apply { + end = transcriptionWord.end + start = transcriptionWord.start + word = transcriptionWord.word + additionalProperties = transcriptionWord.additionalProperties.toMutableMap() + } + + /** End time of the word in seconds. */ + fun end(end: Double) = end(JsonField.of(end)) + + /** End time of the word in seconds. */ + fun end(end: JsonField) = apply { this.end = end } + + /** Start time of the word in seconds. */ + fun start(start: Double) = start(JsonField.of(start)) + + /** Start time of the word in seconds. */ + fun start(start: JsonField) = apply { this.start = start } + + /** The text content of the word. */ + fun word(word: String) = word(JsonField.of(word)) + + /** The text content of the word. */ + fun word(word: JsonField) = apply { this.word = word } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + fun build(): TranscriptionWord = + TranscriptionWord( + checkRequired("end", end), + checkRequired("start", start), + checkRequired("word", word), + additionalProperties.toImmutable(), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is TranscriptionWord && end == other.end && start == other.start && word == other.word && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(end, start, word, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "TranscriptionWord{end=$end, start=$start, word=$word, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/Translation.kt b/openai-java-core/src/main/kotlin/com/openai/models/Translation.kt new file mode 100644 index 000000000..b619c02a6 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/Translation.kt @@ -0,0 +1,107 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.openai.core.ExcludeMissing +import com.openai.core.JsonField +import com.openai.core.JsonMissing +import com.openai.core.JsonValue +import com.openai.core.NoAutoDetect +import com.openai.core.checkRequired +import com.openai.core.immutableEmptyMap +import com.openai.core.toImmutable +import java.util.Objects + +@NoAutoDetect +class Translation +@JsonCreator +private constructor( + @JsonProperty("text") @ExcludeMissing private val text: JsonField = JsonMissing.of(), + @JsonAnySetter private val additionalProperties: Map = immutableEmptyMap(), +) { + + fun text(): String = text.getRequired("text") + + @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + private var validated: Boolean = false + + fun validate(): Translation = apply { + if (validated) { + return@apply + } + + text() + validated = true + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Translation]. */ + class Builder internal constructor() { + + private var text: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(translation: Translation) = apply { + text = translation.text + additionalProperties = translation.additionalProperties.toMutableMap() + } + + fun text(text: String) = text(JsonField.of(text)) + + fun text(text: JsonField) = apply { this.text = text } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + fun build(): Translation = + Translation(checkRequired("text", text), additionalProperties.toImmutable()) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Translation && text == other.text && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(text, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Translation{text=$text, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/TranslationVerbose.kt b/openai-java-core/src/main/kotlin/com/openai/models/TranslationVerbose.kt new file mode 100644 index 000000000..814627e04 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/TranslationVerbose.kt @@ -0,0 +1,187 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.openai.core.ExcludeMissing +import com.openai.core.JsonField +import com.openai.core.JsonMissing +import com.openai.core.JsonValue +import com.openai.core.NoAutoDetect +import com.openai.core.checkKnown +import com.openai.core.checkRequired +import com.openai.core.immutableEmptyMap +import com.openai.core.toImmutable +import java.util.Objects +import java.util.Optional + +@NoAutoDetect +class TranslationVerbose +@JsonCreator +private constructor( + @JsonProperty("duration") + @ExcludeMissing + private val duration: JsonField = JsonMissing.of(), + @JsonProperty("language") + @ExcludeMissing + private val language: JsonField = JsonMissing.of(), + @JsonProperty("text") @ExcludeMissing private val text: JsonField = JsonMissing.of(), + @JsonProperty("segments") + @ExcludeMissing + private val segments: JsonField> = JsonMissing.of(), + @JsonAnySetter private val additionalProperties: Map = immutableEmptyMap(), +) { + + /** The duration of the input audio. */ + fun duration(): Double = duration.getRequired("duration") + + /** The language of the output translation (always `english`). */ + fun language(): String = language.getRequired("language") + + /** The translated text. */ + fun text(): String = text.getRequired("text") + + /** Segments of the translated text and their corresponding details. */ + fun segments(): Optional> = + Optional.ofNullable(segments.getNullable("segments")) + + /** The duration of the input audio. */ + @JsonProperty("duration") @ExcludeMissing fun _duration(): JsonField = duration + + /** The language of the output translation (always `english`). */ + @JsonProperty("language") @ExcludeMissing fun _language(): JsonField = language + + /** The translated text. */ + @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text + + /** Segments of the translated text and their corresponding details. */ + @JsonProperty("segments") + @ExcludeMissing + fun _segments(): JsonField> = segments + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + private var validated: Boolean = false + + fun validate(): TranslationVerbose = apply { + if (validated) { + return@apply + } + + duration() + language() + text() + segments().ifPresent { it.forEach { it.validate() } } + validated = true + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [TranslationVerbose]. */ + class Builder internal constructor() { + + private var duration: JsonField? = null + private var language: JsonField? = null + private var text: JsonField? = null + private var segments: JsonField>? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(translationVerbose: TranslationVerbose) = apply { + duration = translationVerbose.duration + language = translationVerbose.language + text = translationVerbose.text + segments = translationVerbose.segments.map { it.toMutableList() } + additionalProperties = translationVerbose.additionalProperties.toMutableMap() + } + + /** The duration of the input audio. */ + fun duration(duration: Double) = duration(JsonField.of(duration)) + + /** The duration of the input audio. */ + fun duration(duration: JsonField) = apply { this.duration = duration } + + /** The language of the output translation (always `english`). */ + fun language(language: String) = language(JsonField.of(language)) + + /** The language of the output translation (always `english`). */ + fun language(language: JsonField) = apply { this.language = language } + + /** The translated text. */ + fun text(text: String) = text(JsonField.of(text)) + + /** The translated text. */ + fun text(text: JsonField) = apply { this.text = text } + + /** Segments of the translated text and their corresponding details. */ + fun segments(segments: List) = segments(JsonField.of(segments)) + + /** Segments of the translated text and their corresponding details. */ + fun segments(segments: JsonField>) = apply { + this.segments = segments.map { it.toMutableList() } + } + + /** Segments of the translated text and their corresponding details. */ + fun addSegment(segment: TranscriptionSegment) = apply { + segments = + (segments ?: JsonField.of(mutableListOf())).also { + checkKnown("segments", it).add(segment) + } + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + fun build(): TranslationVerbose = + TranslationVerbose( + checkRequired("duration", duration), + checkRequired("language", language), + checkRequired("text", text), + (segments ?: JsonMissing.of()).map { it.toImmutable() }, + additionalProperties.toImmutable(), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is TranslationVerbose && duration == other.duration && language == other.language && text == other.text && segments == other.segments && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(duration, language, text, segments, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "TranslationVerbose{duration=$duration, language=$language, text=$text, segments=$segments, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/UploadCompleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/UploadCompleteParams.kt index 3c42bbe12..2962ef708 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/UploadCompleteParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/UploadCompleteParams.kt @@ -12,6 +12,7 @@ import com.openai.core.JsonMissing import com.openai.core.JsonValue import com.openai.core.NoAutoDetect import com.openai.core.Params +import com.openai.core.checkKnown import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams @@ -157,14 +158,8 @@ private constructor( /** The ordered list of Part IDs. */ fun addPartId(partId: String) = apply { partIds = - (partIds ?: JsonField.of(mutableListOf())).apply { - asKnown() - .orElseThrow { - IllegalStateException( - "Field was set to non-list type: ${javaClass.simpleName}" - ) - } - .add(partId) + (partIds ?: JsonField.of(mutableListOf())).also { + checkKnown("partIds", it).add(partId) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/UploadPartCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/UploadPartCreateParams.kt new file mode 100644 index 000000000..a5a311609 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/UploadPartCreateParams.kt @@ -0,0 +1,270 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.fasterxml.jackson.annotation.JsonCreator +import com.openai.core.MultipartField +import com.openai.core.NoAutoDetect +import com.openai.core.Params +import com.openai.core.checkRequired +import com.openai.core.http.Headers +import com.openai.core.http.QueryParams +import com.openai.core.toImmutable +import java.util.Objects + +/** + * Adds a [Part](https://platform.openai.com/docs/api-reference/uploads/part-object) to an + * [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object. A Part represents + * a chunk of bytes from the file you are trying to upload. + * + * Each Part can be at most 64 MB, and you can add Parts until you hit the Upload maximum of 8 GB. + * + * It is possible to add multiple Parts in parallel. You can decide the intended order of the Parts + * when you [complete the Upload](https://platform.openai.com/docs/api-reference/uploads/complete). + */ +class UploadPartCreateParams +private constructor( + private val uploadId: String, + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun uploadId(): String = uploadId + + /** The chunk of bytes for this Part. */ + fun data(): ByteArray = body.data() + + /** The chunk of bytes for this Part. */ + fun _data(): MultipartField = body._data() + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + @JvmSynthetic + internal fun _body(): Map> = mapOf("data" to _data()).toImmutable() + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + fun getPathParam(index: Int): String { + return when (index) { + 0 -> uploadId + else -> "" + } + } + + @NoAutoDetect + class Body @JsonCreator private constructor(private val data: MultipartField) { + + /** The chunk of bytes for this Part. */ + fun data(): ByteArray = data.value.getRequired("data") + + /** The chunk of bytes for this Part. */ + fun _data(): MultipartField = data + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + data() + validated = true + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var data: MultipartField? = null + + @JvmSynthetic internal fun from(body: Body) = apply { data = body.data } + + /** The chunk of bytes for this Part. */ + fun data(data: ByteArray) = data(MultipartField.of(data)) + + /** The chunk of bytes for this Part. */ + fun data(data: MultipartField) = apply { this.data = data } + + fun build(): Body = Body(checkRequired("data", data)) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && data == other.data /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(data) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Body{data=$data}" + } + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UploadPartCreateParams]. */ + @NoAutoDetect + class Builder internal constructor() { + + private var uploadId: String? = null + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(uploadPartCreateParams: UploadPartCreateParams) = apply { + uploadId = uploadPartCreateParams.uploadId + body = uploadPartCreateParams.body.toBuilder() + additionalHeaders = uploadPartCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = uploadPartCreateParams.additionalQueryParams.toBuilder() + } + + fun uploadId(uploadId: String) = apply { this.uploadId = uploadId } + + /** The chunk of bytes for this Part. */ + fun data(data: ByteArray) = apply { body.data(data) } + + /** The chunk of bytes for this Part. */ + fun data(data: MultipartField) = apply { body.data(data) } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + fun build(): UploadPartCreateParams = + UploadPartCreateParams( + checkRequired("uploadId", uploadId), + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is UploadPartCreateParams && uploadId == other.uploadId && body == other.body && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(uploadId, body, additionalHeaders, additionalQueryParams) /* spotless:on */ + + override fun toString() = + "UploadPartCreateParams{uploadId=$uploadId, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/AudioServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/AudioServiceAsync.kt new file mode 100644 index 000000000..ee89ecfdd --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/AudioServiceAsync.kt @@ -0,0 +1,31 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async + +import com.openai.services.async.audio.SpeechServiceAsync +import com.openai.services.async.audio.TranscriptionServiceAsync +import com.openai.services.async.audio.TranslationServiceAsync + +interface AudioServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + fun transcriptions(): TranscriptionServiceAsync + + fun translations(): TranslationServiceAsync + + fun speech(): SpeechServiceAsync + + /** A view of [AudioServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun transcriptions(): TranscriptionServiceAsync.WithRawResponse + + fun translations(): TranslationServiceAsync.WithRawResponse + + fun speech(): SpeechServiceAsync.WithRawResponse + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/AudioServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/AudioServiceAsyncImpl.kt new file mode 100644 index 000000000..d809c8755 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/AudioServiceAsyncImpl.kt @@ -0,0 +1,59 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async + +import com.openai.core.ClientOptions +import com.openai.services.async.audio.SpeechServiceAsync +import com.openai.services.async.audio.SpeechServiceAsyncImpl +import com.openai.services.async.audio.TranscriptionServiceAsync +import com.openai.services.async.audio.TranscriptionServiceAsyncImpl +import com.openai.services.async.audio.TranslationServiceAsync +import com.openai.services.async.audio.TranslationServiceAsyncImpl + +class AudioServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + AudioServiceAsync { + + private val withRawResponse: AudioServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val transcriptions: TranscriptionServiceAsync by lazy { + TranscriptionServiceAsyncImpl(clientOptions) + } + + private val translations: TranslationServiceAsync by lazy { + TranslationServiceAsyncImpl(clientOptions) + } + + private val speech: SpeechServiceAsync by lazy { SpeechServiceAsyncImpl(clientOptions) } + + override fun withRawResponse(): AudioServiceAsync.WithRawResponse = withRawResponse + + override fun transcriptions(): TranscriptionServiceAsync = transcriptions + + override fun translations(): TranslationServiceAsync = translations + + override fun speech(): SpeechServiceAsync = speech + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + AudioServiceAsync.WithRawResponse { + + private val transcriptions: TranscriptionServiceAsync.WithRawResponse by lazy { + TranscriptionServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val translations: TranslationServiceAsync.WithRawResponse by lazy { + TranslationServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val speech: SpeechServiceAsync.WithRawResponse by lazy { + SpeechServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun transcriptions(): TranscriptionServiceAsync.WithRawResponse = transcriptions + + override fun translations(): TranslationServiceAsync.WithRawResponse = translations + + override fun speech(): SpeechServiceAsync.WithRawResponse = speech + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/BatchServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/BatchServiceAsync.kt index f8fd8488f..9f96b4c8f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/BatchServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/BatchServiceAsync.kt @@ -4,7 +4,9 @@ package com.openai.services.async +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.Batch import com.openai.models.BatchCancelParams import com.openai.models.BatchCreateParams @@ -15,6 +17,11 @@ import java.util.concurrent.CompletableFuture interface BatchServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** Creates and executes a batch from an uploaded file of requests */ @JvmOverloads fun create( @@ -50,4 +57,62 @@ interface BatchServiceAsync { params: BatchCancelParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** A view of [BatchServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /batches`, but is otherwise the same as + * [BatchServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BatchCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /batches/{batch_id}`, but is otherwise the same as + * [BatchServiceAsync.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BatchRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /batches`, but is otherwise the same as + * [BatchServiceAsync.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: BatchListParams = BatchListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /batches`, but is otherwise the same as + * [BatchServiceAsync.list]. + */ + @MustBeClosed + fun list( + requestOptions: RequestOptions + ): CompletableFuture> = + list(BatchListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /batches/{batch_id}/cancel`, but is otherwise the + * same as [BatchServiceAsync.cancel]. + */ + @JvmOverloads + @MustBeClosed + fun cancel( + params: BatchCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/BatchServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/BatchServiceAsyncImpl.kt index c54ba677a..1054bbc05 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/BatchServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/BatchServiceAsyncImpl.kt @@ -10,7 +10,9 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.Batch @@ -24,121 +26,169 @@ import java.util.concurrent.CompletableFuture class BatchServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : BatchServiceAsync { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: BatchServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun withRawResponse(): BatchServiceAsync.WithRawResponse = withRawResponse - /** Creates and executes a batch from an uploaded file of requests */ override fun create( params: BatchCreateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("batches") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // post /batches + withRawResponse().create(params, requestOptions).thenApply { it.parse() } - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Retrieves a batch. */ override fun retrieve( params: BatchRetrieveParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("batches", params.getPathParam(0)) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // get /batches/{batch_id} + withRawResponse().retrieve(params, requestOptions).thenApply { it.parse() } - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) - - /** List your organization's batches. */ override fun list( params: BatchListParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("batches") - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - .let { BatchListPageAsync.of(this, params, it) } - } - } + ): CompletableFuture = + // get /batches + withRawResponse().list(params, requestOptions).thenApply { it.parse() } - private val cancelHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Cancels an in-progress batch. The batch will be in status `cancelling` for up to 10 minutes, - * before changing to `cancelled`, where it will have partial results (if any) available in the - * output file. - */ override fun cancel( params: BatchCancelParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("batches", params.getPathParam(0), "cancel") - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { cancelHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CompletableFuture = + // post /batches/{batch_id}/cancel + withRawResponse().cancel(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + BatchServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: BatchCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("batches") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BatchRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("batches", params.getPathParam(0)) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: BatchListParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("batches") + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + BatchListPageAsync.of( + BatchServiceAsyncImpl(clientOptions), + params, + it, + ) + } + } + } + } + + private val cancelHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun cancel( + params: BatchCancelParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("batches", params.getPathParam(0), "cancel") + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { cancelHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/BetaServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/BetaServiceAsync.kt index e6fc24f62..fad15c9b0 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/BetaServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/BetaServiceAsync.kt @@ -8,9 +8,24 @@ import com.openai.services.async.beta.VectorStoreServiceAsync interface BetaServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun vectorStores(): VectorStoreServiceAsync fun assistants(): AssistantServiceAsync fun threads(): ThreadServiceAsync + + /** A view of [BetaServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun vectorStores(): VectorStoreServiceAsync.WithRawResponse + + fun assistants(): AssistantServiceAsync.WithRawResponse + + fun threads(): ThreadServiceAsync.WithRawResponse + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/BetaServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/BetaServiceAsyncImpl.kt index c161ebc6c..e153842f0 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/BetaServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/BetaServiceAsyncImpl.kt @@ -13,6 +13,10 @@ import com.openai.services.async.beta.VectorStoreServiceAsyncImpl class BetaServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : BetaServiceAsync { + private val withRawResponse: BetaServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + private val vectorStores: VectorStoreServiceAsync by lazy { VectorStoreServiceAsyncImpl(clientOptions) } @@ -23,9 +27,33 @@ class BetaServiceAsyncImpl internal constructor(private val clientOptions: Clien private val threads: ThreadServiceAsync by lazy { ThreadServiceAsyncImpl(clientOptions) } + override fun withRawResponse(): BetaServiceAsync.WithRawResponse = withRawResponse + override fun vectorStores(): VectorStoreServiceAsync = vectorStores override fun assistants(): AssistantServiceAsync = assistants override fun threads(): ThreadServiceAsync = threads + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + BetaServiceAsync.WithRawResponse { + + private val vectorStores: VectorStoreServiceAsync.WithRawResponse by lazy { + VectorStoreServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val assistants: AssistantServiceAsync.WithRawResponse by lazy { + AssistantServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val threads: ThreadServiceAsync.WithRawResponse by lazy { + ThreadServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun vectorStores(): VectorStoreServiceAsync.WithRawResponse = vectorStores + + override fun assistants(): AssistantServiceAsync.WithRawResponse = assistants + + override fun threads(): ThreadServiceAsync.WithRawResponse = threads + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/ChatServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/ChatServiceAsync.kt index ab900e5ed..faf658eb1 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/ChatServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/ChatServiceAsync.kt @@ -6,5 +6,16 @@ import com.openai.services.async.chat.CompletionServiceAsync interface ChatServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun completions(): CompletionServiceAsync + + /** A view of [ChatServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun completions(): CompletionServiceAsync.WithRawResponse + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/ChatServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/ChatServiceAsyncImpl.kt index e8b7de079..b5b278cff 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/ChatServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/ChatServiceAsyncImpl.kt @@ -9,9 +9,25 @@ import com.openai.services.async.chat.CompletionServiceAsyncImpl class ChatServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : ChatServiceAsync { + private val withRawResponse: ChatServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + private val completions: CompletionServiceAsync by lazy { CompletionServiceAsyncImpl(clientOptions) } + override fun withRawResponse(): ChatServiceAsync.WithRawResponse = withRawResponse + override fun completions(): CompletionServiceAsync = completions + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ChatServiceAsync.WithRawResponse { + + private val completions: CompletionServiceAsync.WithRawResponse by lazy { + CompletionServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun completions(): CompletionServiceAsync.WithRawResponse = completions + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/CompletionServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/CompletionServiceAsync.kt index 7248bcd93..0e8444b99 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/CompletionServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/CompletionServiceAsync.kt @@ -4,14 +4,22 @@ package com.openai.services.async +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions import com.openai.core.http.AsyncStreamResponse +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.StreamResponse import com.openai.models.Completion import com.openai.models.CompletionCreateParams import java.util.concurrent.CompletableFuture interface CompletionServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** Creates a completion for the provided prompt and parameters. */ @JvmOverloads fun create( @@ -25,4 +33,33 @@ interface CompletionServiceAsync { params: CompletionCreateParams, requestOptions: RequestOptions = RequestOptions.none(), ): AsyncStreamResponse + + /** + * A view of [CompletionServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /completions`, but is otherwise the same as + * [CompletionServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: CompletionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /completions`, but is otherwise the same as + * [CompletionServiceAsync.createStreaming]. + */ + @JvmOverloads + @MustBeClosed + fun createStreaming( + params: CompletionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/CompletionServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/CompletionServiceAsyncImpl.kt index 401d1fad8..86162d162 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/CompletionServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/CompletionServiceAsyncImpl.kt @@ -14,10 +14,12 @@ import com.openai.core.http.AsyncStreamResponse import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor import com.openai.core.http.StreamResponse +import com.openai.core.http.json import com.openai.core.http.map +import com.openai.core.http.parseable import com.openai.core.http.toAsync -import com.openai.core.json import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.Completion @@ -27,73 +29,105 @@ import java.util.concurrent.CompletableFuture class CompletionServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : CompletionServiceAsync { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: CompletionServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun withRawResponse(): CompletionServiceAsync.WithRawResponse = withRawResponse - /** Creates a completion for the provided prompt and parameters. */ override fun create( params: CompletionCreateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("completions") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, params.model().toString()) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val createStreamingHandler: Handler> = - sseHandler(clientOptions.jsonMapper).mapJson().withErrorHandler(errorHandler) + ): CompletableFuture = + // post /completions + withRawResponse().create(params, requestOptions).thenApply { it.parse() } - /** Creates a completion for the provided prompt and parameters. */ override fun createStreaming( params: CompletionCreateParams, requestOptions: RequestOptions, - ): AsyncStreamResponse { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("completions") - .body( - json( - clientOptions.jsonMapper, - params - ._body() - .toBuilder() - .putAdditionalProperty("stream", JsonValue.from(true)) - .build(), + ): AsyncStreamResponse = + // post /completions + withRawResponse() + .createStreaming(params, requestOptions) + .thenApply { it.parse() } + .toAsync(clientOptions.streamHandlerExecutor) + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CompletionServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: CompletionCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("completions") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val createStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper) + .mapJson() + .withErrorHandler(errorHandler) + + override fun createStreaming( + params: CompletionCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture>> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("completions") + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("stream", JsonValue.from(true)) + .build(), + ) ) - ) - .build() - .prepareAsync(clientOptions, params, params.model().toString()) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .let { createStreamingHandler.handle(it) } - .let { streamResponse -> - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - streamResponse.map { it.validate() } - } else { - streamResponse - } + .build() + .prepareAsync(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .let { createStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } } - } - .toAsync(clientOptions.streamHandlerExecutor) + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/EmbeddingServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/EmbeddingServiceAsync.kt index c21a831a5..454e588af 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/EmbeddingServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/EmbeddingServiceAsync.kt @@ -4,17 +4,41 @@ package com.openai.services.async +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.CreateEmbeddingResponse import com.openai.models.EmbeddingCreateParams import java.util.concurrent.CompletableFuture interface EmbeddingServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** Creates an embedding vector representing the input text. */ @JvmOverloads fun create( params: EmbeddingCreateParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** + * A view of [EmbeddingServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /embeddings`, but is otherwise the same as + * [EmbeddingServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: EmbeddingCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/EmbeddingServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/EmbeddingServiceAsyncImpl.kt index 851261224..d7284eae5 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/EmbeddingServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/EmbeddingServiceAsyncImpl.kt @@ -10,7 +10,9 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.CreateEmbeddingResponse @@ -20,34 +22,53 @@ import java.util.concurrent.CompletableFuture class EmbeddingServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : EmbeddingServiceAsync { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: EmbeddingServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + override fun withRawResponse(): EmbeddingServiceAsync.WithRawResponse = withRawResponse - /** Creates an embedding vector representing the input text. */ override fun create( params: EmbeddingCreateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("embeddings") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, params.model().toString()) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CompletableFuture = + // post /embeddings + withRawResponse().create(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + EmbeddingServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun create( + params: EmbeddingCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("embeddings") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/FileServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/FileServiceAsync.kt index 2b6d3e55d..a2163c6a7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/FileServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/FileServiceAsync.kt @@ -7,7 +7,9 @@ package com.openai.services.async import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions import com.openai.core.http.HttpResponse +import com.openai.core.http.HttpResponseFor import com.openai.models.FileContentParams +import com.openai.models.FileCreateParams import com.openai.models.FileDeleteParams import com.openai.models.FileDeleted import com.openai.models.FileListPageAsync @@ -18,6 +20,36 @@ import java.util.concurrent.CompletableFuture interface FileServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Upload a file that can be used across various endpoints. Individual files can be up to 512 + * MB, and the size of all files uploaded by one organization can be up to 100 GB. + * + * The Assistants API supports files up to 2 million tokens and of specific file types. See the + * [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) for details. + * + * The Fine-tuning API only supports `.jsonl` files. The input also has certain required formats + * for fine-tuning [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) + * or + * [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) + * models. + * + * The Batch API only supports `.jsonl` files up to 200 MB in size. The input also has a + * specific required + * [format](https://platform.openai.com/docs/api-reference/batch/request-input). + * + * Please [contact us](https://help.openai.com/) if you need to increase these storage limits. + */ + @JvmOverloads + fun create( + params: FileCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + /** Returns information about a specific file. */ @JvmOverloads fun retrieve( @@ -50,4 +82,73 @@ interface FileServiceAsync { params: FileContentParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** A view of [FileServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /files`, but is otherwise the same as + * [FileServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: FileCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /files/{file_id}`, but is otherwise the same as + * [FileServiceAsync.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: FileRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /files`, but is otherwise the same as + * [FileServiceAsync.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: FileListParams = FileListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /files`, but is otherwise the same as + * [FileServiceAsync.list]. + */ + @MustBeClosed + fun list( + requestOptions: RequestOptions + ): CompletableFuture> = + list(FileListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /files/{file_id}`, but is otherwise the same as + * [FileServiceAsync.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: FileDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /files/{file_id}/content`, but is otherwise the same + * as [FileServiceAsync.content]. + */ + @JvmOverloads + @MustBeClosed + fun content( + params: FileContentParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/FileServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/FileServiceAsyncImpl.kt index 77e3f44f5..927c18747 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/FileServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/FileServiceAsyncImpl.kt @@ -11,10 +11,14 @@ import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.multipartFormData +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.FileContentParams +import com.openai.models.FileCreateParams import com.openai.models.FileDeleteParams import com.openai.models.FileDeleted import com.openai.models.FileListPageAsync @@ -26,105 +30,192 @@ import java.util.concurrent.CompletableFuture class FileServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : FileServiceAsync { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: FileServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): FileServiceAsync.WithRawResponse = withRawResponse - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun create( + params: FileCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /files + withRawResponse().create(params, requestOptions).thenApply { it.parse() } - /** Returns information about a specific file. */ override fun retrieve( params: FileRetrieveParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("files", params.getPathParam(0)) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + ): CompletableFuture = + // get /files/{file_id} + withRawResponse().retrieve(params, requestOptions).thenApply { it.parse() } - /** Returns a list of files. */ override fun list( params: FileListParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("files") - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - .let { FileListPageAsync.of(this, params, it) } - } - } - - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // get /files + withRawResponse().list(params, requestOptions).thenApply { it.parse() } - /** Delete a file. */ override fun delete( params: FileDeleteParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments("files", params.getPathParam(0)) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // delete /files/{file_id} + withRawResponse().delete(params, requestOptions).thenApply { it.parse() } - /** Returns the contents of the specified file. */ override fun content( params: FileContentParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("files", params.getPathParam(0), "content") - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request.thenComposeAsync { - clientOptions.httpClient.executeAsync(it, requestOptions) + ): CompletableFuture = + // get /files/{file_id}/content + withRawResponse().content(params, requestOptions) + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + FileServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: FileCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("files") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: FileRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("files", params.getPathParam(0)) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: FileListParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("files") + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + FileListPageAsync.of( + FileServiceAsyncImpl(clientOptions), + params, + it, + ) + } + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun delete( + params: FileDeleteParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments("files", params.getPathParam(0)) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + override fun content( + params: FileContentParams, + requestOptions: RequestOptions, + ): CompletableFuture { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("files", params.getPathParam(0), "content") + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request.thenComposeAsync { + clientOptions.httpClient.executeAsync(it, requestOptions) + } } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/FineTuningServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/FineTuningServiceAsync.kt index 6cdd3f0a2..4d9cf6c5f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/FineTuningServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/FineTuningServiceAsync.kt @@ -6,5 +6,19 @@ import com.openai.services.async.fineTuning.JobServiceAsync interface FineTuningServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun jobs(): JobServiceAsync + + /** + * A view of [FineTuningServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + fun jobs(): JobServiceAsync.WithRawResponse + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/FineTuningServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/FineTuningServiceAsyncImpl.kt index 62bb532e6..509eecc52 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/FineTuningServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/FineTuningServiceAsyncImpl.kt @@ -9,7 +9,23 @@ import com.openai.services.async.fineTuning.JobServiceAsyncImpl class FineTuningServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : FineTuningServiceAsync { + private val withRawResponse: FineTuningServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + private val jobs: JobServiceAsync by lazy { JobServiceAsyncImpl(clientOptions) } + override fun withRawResponse(): FineTuningServiceAsync.WithRawResponse = withRawResponse + override fun jobs(): JobServiceAsync = jobs + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + FineTuningServiceAsync.WithRawResponse { + + private val jobs: JobServiceAsync.WithRawResponse by lazy { + JobServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun jobs(): JobServiceAsync.WithRawResponse = jobs + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/ImageServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/ImageServiceAsync.kt index 0f13635d1..b4b8096d2 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/ImageServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/ImageServiceAsync.kt @@ -4,17 +4,77 @@ package com.openai.services.async +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor +import com.openai.models.ImageCreateVariationParams +import com.openai.models.ImageEditParams import com.openai.models.ImageGenerateParams import com.openai.models.ImagesResponse import java.util.concurrent.CompletableFuture interface ImageServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** Creates a variation of a given image. */ + @JvmOverloads + fun createVariation( + params: ImageCreateVariationParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** Creates an edited or extended image given an original image and a prompt. */ + @JvmOverloads + fun edit( + params: ImageEditParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + /** Creates an image given a prompt. */ @JvmOverloads fun generate( params: ImageGenerateParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** A view of [ImageServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /images/variations`, but is otherwise the same as + * [ImageServiceAsync.createVariation]. + */ + @JvmOverloads + @MustBeClosed + fun createVariation( + params: ImageCreateVariationParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /images/edits`, but is otherwise the same as + * [ImageServiceAsync.edit]. + */ + @JvmOverloads + @MustBeClosed + fun edit( + params: ImageEditParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /images/generations`, but is otherwise the same as + * [ImageServiceAsync.generate]. + */ + @JvmOverloads + @MustBeClosed + fun generate( + params: ImageGenerateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/ImageServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/ImageServiceAsyncImpl.kt index 16bcbce93..edf4e3fe1 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/ImageServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/ImageServiceAsyncImpl.kt @@ -10,9 +10,14 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.multipartFormData +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError +import com.openai.models.ImageCreateVariationParams +import com.openai.models.ImageEditParams import com.openai.models.ImageGenerateParams import com.openai.models.ImagesResponse import java.util.concurrent.CompletableFuture @@ -20,33 +25,134 @@ import java.util.concurrent.CompletableFuture class ImageServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : ImageServiceAsync { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: ImageServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): ImageServiceAsync.WithRawResponse = withRawResponse + + override fun createVariation( + params: ImageCreateVariationParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /images/variations + withRawResponse().createVariation(params, requestOptions).thenApply { it.parse() } - private val generateHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun edit( + params: ImageEditParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /images/edits + withRawResponse().edit(params, requestOptions).thenApply { it.parse() } - /** Creates an image given a prompt. */ override fun generate( params: ImageGenerateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("images", "generations") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, params.model().toString()) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { generateHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CompletableFuture = + // post /images/generations + withRawResponse().generate(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ImageServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createVariationHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun createVariation( + params: ImageCreateVariationParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("images", "variations") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync( + clientOptions, + params, + deploymentModel = params.model().map { it.toString() }.orElse(null), + ) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createVariationHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val editHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun edit( + params: ImageEditParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("images", "edits") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync( + clientOptions, + params, + deploymentModel = params.model().map { it.toString() }.orElse(null), + ) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { editHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val generateHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun generate( + params: ImageGenerateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("images", "generations") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { generateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/ModelServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/ModelServiceAsync.kt index 532abc29d..550ee7b76 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/ModelServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/ModelServiceAsync.kt @@ -4,7 +4,9 @@ package com.openai.services.async +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.Model import com.openai.models.ModelDeleteParams import com.openai.models.ModelDeleted @@ -15,6 +17,11 @@ import java.util.concurrent.CompletableFuture interface ModelServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** * Retrieves a model instance, providing basic information about the model such as the owner and * permissioning. @@ -51,4 +58,51 @@ interface ModelServiceAsync { params: ModelDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** A view of [ModelServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `get /models/{model}`, but is otherwise the same as + * [ModelServiceAsync.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: ModelRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /models`, but is otherwise the same as + * [ModelServiceAsync.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: ModelListParams = ModelListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /models`, but is otherwise the same as + * [ModelServiceAsync.list]. + */ + @MustBeClosed + fun list( + requestOptions: RequestOptions + ): CompletableFuture> = + list(ModelListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /models/{model}`, but is otherwise the same as + * [ModelServiceAsync.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: ModelDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/ModelServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/ModelServiceAsyncImpl.kt index cf108aa77..7f20ec1ed 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/ModelServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/ModelServiceAsyncImpl.kt @@ -10,7 +10,9 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.Model @@ -24,98 +26,132 @@ import java.util.concurrent.CompletableFuture class ModelServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : ModelServiceAsync { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: ModelServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun withRawResponse(): ModelServiceAsync.WithRawResponse = withRawResponse - /** - * Retrieves a model instance, providing basic information about the model such as the owner and - * permissioning. - */ override fun retrieve( params: ModelRetrieveParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("models", params.getPathParam(0)) - .build() - .prepareAsync(clientOptions, params, params.model()) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // get /models/{model} + withRawResponse().retrieve(params, requestOptions).thenApply { it.parse() } - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) - - /** - * Lists the currently available models, and provides basic information about each one such as - * the owner and availability. - */ override fun list( params: ModelListParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("models") - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - .let { ModelListPageAsync.of(this, params, it) } - } - } - - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // get /models + withRawResponse().list(params, requestOptions).thenApply { it.parse() } - /** - * Delete a fine-tuned model. You must have the Owner role in your organization to delete a - * model. - */ override fun delete( params: ModelDeleteParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments("models", params.getPathParam(0)) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepareAsync(clientOptions, params, params.model()) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CompletableFuture = + // delete /models/{model} + withRawResponse().delete(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ModelServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: ModelRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("models", params.getPathParam(0)) + .build() + .prepareAsync(clientOptions, params, params.model()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: ModelListParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("models") + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + ModelListPageAsync.of( + ModelServiceAsyncImpl(clientOptions), + params, + it, + ) + } + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun delete( + params: ModelDeleteParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments("models", params.getPathParam(0)) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params, params.model()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/ModerationServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/ModerationServiceAsync.kt index 0359f8c80..236642721 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/ModerationServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/ModerationServiceAsync.kt @@ -4,13 +4,20 @@ package com.openai.services.async +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.ModerationCreateParams import com.openai.models.ModerationCreateResponse import java.util.concurrent.CompletableFuture interface ModerationServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** * Classifies if text and/or image inputs are potentially harmful. Learn more in the * [moderation guide](https://platform.openai.com/docs/guides/moderation). @@ -20,4 +27,22 @@ interface ModerationServiceAsync { params: ModerationCreateParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** + * A view of [ModerationServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /moderations`, but is otherwise the same as + * [ModerationServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: ModerationCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/ModerationServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/ModerationServiceAsyncImpl.kt index 538545353..4ba5b131e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/ModerationServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/ModerationServiceAsyncImpl.kt @@ -10,7 +10,9 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.ModerationCreateParams @@ -20,41 +22,57 @@ import java.util.concurrent.CompletableFuture class ModerationServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : ModerationServiceAsync { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: ModerationServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + override fun withRawResponse(): ModerationServiceAsync.WithRawResponse = withRawResponse - /** - * Classifies if text and/or image inputs are potentially harmful. Learn more in the - * [moderation guide](https://platform.openai.com/docs/guides/moderation). - */ override fun create( params: ModerationCreateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("moderations") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync( - clientOptions, - params, - params.model().map { it.toString() }.orElse(null), - ) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CompletableFuture = + // post /moderations + withRawResponse().create(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ModerationServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun create( + params: ModerationCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("moderations") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync( + clientOptions, + params, + params.model().map { it.toString() }.orElse(null), + ) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/UploadServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/UploadServiceAsync.kt index 5c8a437ff..610a6e265 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/UploadServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/UploadServiceAsync.kt @@ -4,7 +4,9 @@ package com.openai.services.async +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.Upload import com.openai.models.UploadCancelParams import com.openai.models.UploadCompleteParams @@ -14,6 +16,11 @@ import java.util.concurrent.CompletableFuture interface UploadServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun parts(): PartServiceAsync /** @@ -67,4 +74,45 @@ interface UploadServiceAsync { params: UploadCompleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** + * A view of [UploadServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + fun parts(): PartServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /uploads`, but is otherwise the same as + * [UploadServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: UploadCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /uploads/{upload_id}/cancel`, but is otherwise the + * same as [UploadServiceAsync.cancel]. + */ + @JvmOverloads + @MustBeClosed + fun cancel( + params: UploadCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /uploads/{upload_id}/complete`, but is otherwise + * the same as [UploadServiceAsync.complete]. + */ + @JvmOverloads + @MustBeClosed + fun complete( + params: UploadCompleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/UploadServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/UploadServiceAsyncImpl.kt index 8df8d8f33..f554b9c01 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/UploadServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/UploadServiceAsyncImpl.kt @@ -10,7 +10,9 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.Upload @@ -24,124 +26,136 @@ import java.util.concurrent.CompletableFuture class UploadServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : UploadServiceAsync { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: UploadServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } private val parts: PartServiceAsync by lazy { PartServiceAsyncImpl(clientOptions) } + override fun withRawResponse(): UploadServiceAsync.WithRawResponse = withRawResponse + override fun parts(): PartServiceAsync = parts - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Creates an intermediate - * [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object that you can - * add [Parts](https://platform.openai.com/docs/api-reference/uploads/part-object) to. - * Currently, an Upload can accept at most 8 GB in total and expires after an hour after you - * create it. - * - * Once you complete the Upload, we will create a - * [File](https://platform.openai.com/docs/api-reference/files/object) object that contains all - * the parts you uploaded. This File is usable in the rest of our platform as a regular File - * object. - * - * For certain `purpose`s, the correct `mime_type` must be specified. Please refer to - * documentation for the supported MIME types for your use case: - * - [Assistants](https://platform.openai.com/docs/assistants/tools/file-search#supported-files) - * - * For guidance on the proper filename extensions for each purpose, please follow the - * documentation on - * [creating a File](https://platform.openai.com/docs/api-reference/files/create). - */ override fun create( params: UploadCreateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("uploads") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val cancelHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // post /uploads + withRawResponse().create(params, requestOptions).thenApply { it.parse() } - /** Cancels the Upload. No Parts may be added after an Upload is cancelled. */ override fun cancel( params: UploadCancelParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("uploads", params.getPathParam(0), "cancel") - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { cancelHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // post /uploads/{upload_id}/cancel + withRawResponse().cancel(params, requestOptions).thenApply { it.parse() } - private val completeHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Completes the [Upload](https://platform.openai.com/docs/api-reference/uploads/object). - * - * Within the returned Upload object, there is a nested - * [File](https://platform.openai.com/docs/api-reference/files/object) object that is ready to - * use in the rest of the platform. - * - * You can specify the order of the Parts by passing in an ordered list of the Part IDs. - * - * The number of bytes uploaded upon completion must match the number of bytes initially - * specified when creating the Upload object. No Parts may be added after an Upload is - * completed. - */ override fun complete( params: UploadCompleteParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("uploads", params.getPathParam(0), "complete") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { completeHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CompletableFuture = + // post /uploads/{upload_id}/complete + withRawResponse().complete(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + UploadServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val parts: PartServiceAsync.WithRawResponse by lazy { + PartServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun parts(): PartServiceAsync.WithRawResponse = parts + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: UploadCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("uploads") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val cancelHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun cancel( + params: UploadCancelParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("uploads", params.getPathParam(0), "cancel") + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { cancelHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val completeHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun complete( + params: UploadCompleteParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("uploads", params.getPathParam(0), "complete") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { completeHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/audio/SpeechServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/audio/SpeechServiceAsync.kt new file mode 100644 index 000000000..c001118f3 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/audio/SpeechServiceAsync.kt @@ -0,0 +1,44 @@ +// File generated from our OpenAPI spec by Stainless. + +@file:Suppress("OVERLOADS_INTERFACE") // See https://youtrack.jetbrains.com/issue/KT-36102 + +package com.openai.services.async.audio + +import com.google.errorprone.annotations.MustBeClosed +import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponse +import com.openai.models.AudioSpeechCreateParams +import java.util.concurrent.CompletableFuture + +interface SpeechServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** Generates audio from the input text. */ + @JvmOverloads + @MustBeClosed + fun create( + params: AudioSpeechCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** + * A view of [SpeechServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /audio/speech`, but is otherwise the same as + * [SpeechServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: AudioSpeechCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/audio/SpeechServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/audio/SpeechServiceAsyncImpl.kt new file mode 100644 index 000000000..23597feb5 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/audio/SpeechServiceAsyncImpl.kt @@ -0,0 +1,60 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.audio + +import com.openai.core.ClientOptions +import com.openai.core.RequestOptions +import com.openai.core.handlers.errorHandler +import com.openai.core.http.HttpMethod +import com.openai.core.http.HttpRequest +import com.openai.core.http.HttpResponse +import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.json +import com.openai.core.prepareAsync +import com.openai.errors.OpenAIError +import com.openai.models.AudioSpeechCreateParams +import java.util.concurrent.CompletableFuture + +class SpeechServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + SpeechServiceAsync { + + private val withRawResponse: SpeechServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): SpeechServiceAsync.WithRawResponse = withRawResponse + + override fun create( + params: AudioSpeechCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /audio/speech + withRawResponse().create(params, requestOptions) + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + SpeechServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + override fun create( + params: AudioSpeechCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("audio", "speech") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync( + clientOptions, + params, + deploymentModel = params.model().toString(), + ) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request.thenComposeAsync { + clientOptions.httpClient.executeAsync(it, requestOptions) + } + } + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/audio/TranscriptionServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/audio/TranscriptionServiceAsync.kt new file mode 100644 index 000000000..52cac9b4e --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/audio/TranscriptionServiceAsync.kt @@ -0,0 +1,45 @@ +// File generated from our OpenAPI spec by Stainless. + +@file:Suppress("OVERLOADS_INTERFACE") // See https://youtrack.jetbrains.com/issue/KT-36102 + +package com.openai.services.async.audio + +import com.google.errorprone.annotations.MustBeClosed +import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor +import com.openai.models.AudioTranscriptionCreateParams +import com.openai.models.AudioTranscriptionCreateResponse +import java.util.concurrent.CompletableFuture + +interface TranscriptionServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** Transcribes audio into the input language. */ + @JvmOverloads + fun create( + params: AudioTranscriptionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** + * A view of [TranscriptionServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /audio/transcriptions`, but is otherwise the same + * as [TranscriptionServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: AudioTranscriptionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/audio/TranscriptionServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/audio/TranscriptionServiceAsyncImpl.kt new file mode 100644 index 000000000..db8f84a1e --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/audio/TranscriptionServiceAsyncImpl.kt @@ -0,0 +1,78 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.audio + +import com.openai.core.ClientOptions +import com.openai.core.RequestOptions +import com.openai.core.handlers.errorHandler +import com.openai.core.handlers.jsonHandler +import com.openai.core.handlers.withErrorHandler +import com.openai.core.http.HttpMethod +import com.openai.core.http.HttpRequest +import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.multipartFormData +import com.openai.core.http.parseable +import com.openai.core.prepareAsync +import com.openai.errors.OpenAIError +import com.openai.models.AudioTranscriptionCreateParams +import com.openai.models.AudioTranscriptionCreateResponse +import java.util.concurrent.CompletableFuture + +class TranscriptionServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + TranscriptionServiceAsync { + + private val withRawResponse: TranscriptionServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): TranscriptionServiceAsync.WithRawResponse = withRawResponse + + override fun create( + params: AudioTranscriptionCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /audio/transcriptions + withRawResponse().create(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + TranscriptionServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun create( + params: AudioTranscriptionCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("audio", "transcriptions") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync( + clientOptions, + params, + deploymentModel = params.model().toString(), + ) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/audio/TranslationServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/audio/TranslationServiceAsync.kt new file mode 100644 index 000000000..1d5875f7a --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/audio/TranslationServiceAsync.kt @@ -0,0 +1,45 @@ +// File generated from our OpenAPI spec by Stainless. + +@file:Suppress("OVERLOADS_INTERFACE") // See https://youtrack.jetbrains.com/issue/KT-36102 + +package com.openai.services.async.audio + +import com.google.errorprone.annotations.MustBeClosed +import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor +import com.openai.models.AudioTranslationCreateParams +import com.openai.models.AudioTranslationCreateResponse +import java.util.concurrent.CompletableFuture + +interface TranslationServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** Translates audio into English. */ + @JvmOverloads + fun create( + params: AudioTranslationCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** + * A view of [TranslationServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /audio/translations`, but is otherwise the same as + * [TranslationServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: AudioTranslationCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/audio/TranslationServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/audio/TranslationServiceAsyncImpl.kt new file mode 100644 index 000000000..a02fc95e0 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/audio/TranslationServiceAsyncImpl.kt @@ -0,0 +1,78 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.audio + +import com.openai.core.ClientOptions +import com.openai.core.RequestOptions +import com.openai.core.handlers.errorHandler +import com.openai.core.handlers.jsonHandler +import com.openai.core.handlers.withErrorHandler +import com.openai.core.http.HttpMethod +import com.openai.core.http.HttpRequest +import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.multipartFormData +import com.openai.core.http.parseable +import com.openai.core.prepareAsync +import com.openai.errors.OpenAIError +import com.openai.models.AudioTranslationCreateParams +import com.openai.models.AudioTranslationCreateResponse +import java.util.concurrent.CompletableFuture + +class TranslationServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + TranslationServiceAsync { + + private val withRawResponse: TranslationServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): TranslationServiceAsync.WithRawResponse = withRawResponse + + override fun create( + params: AudioTranslationCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /audio/translations + withRawResponse().create(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + TranslationServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun create( + params: AudioTranslationCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("audio", "translations") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync( + clientOptions, + params, + deploymentModel = params.model().toString(), + ) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/AssistantServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/AssistantServiceAsync.kt index c513e8634..b64a4a8dd 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/AssistantServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/AssistantServiceAsync.kt @@ -4,7 +4,9 @@ package com.openai.services.async.beta +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.Assistant import com.openai.models.AssistantDeleted import com.openai.models.BetaAssistantCreateParams @@ -17,6 +19,11 @@ import java.util.concurrent.CompletableFuture interface AssistantServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** Create an assistant with a model and instructions. */ @JvmOverloads fun create( @@ -55,4 +62,75 @@ interface AssistantServiceAsync { params: BetaAssistantDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** + * A view of [AssistantServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /assistants`, but is otherwise the same as + * [AssistantServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BetaAssistantCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /assistants/{assistant_id}`, but is otherwise the + * same as [AssistantServiceAsync.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaAssistantRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /assistants/{assistant_id}`, but is otherwise the + * same as [AssistantServiceAsync.update]. + */ + @JvmOverloads + @MustBeClosed + fun update( + params: BetaAssistantUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /assistants`, but is otherwise the same as + * [AssistantServiceAsync.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: BetaAssistantListParams = BetaAssistantListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /assistants`, but is otherwise the same as + * [AssistantServiceAsync.list]. + */ + @MustBeClosed + fun list( + requestOptions: RequestOptions + ): CompletableFuture> = + list(BetaAssistantListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /assistants/{assistant_id}`, but is otherwise the + * same as [AssistantServiceAsync.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: BetaAssistantDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/AssistantServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/AssistantServiceAsyncImpl.kt index 1c02b0a3b..13f7de1b8 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/AssistantServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/AssistantServiceAsyncImpl.kt @@ -11,7 +11,9 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.Assistant @@ -32,154 +34,215 @@ class AssistantServiceAsyncImpl internal constructor(private val clientOptions: private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: AssistantServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun withRawResponse(): AssistantServiceAsync.WithRawResponse = withRawResponse - /** Create an assistant with a model and instructions. */ override fun create( params: BetaAssistantCreateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("assistants") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, params.model().toString()) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // post /assistants + withRawResponse().create(params, requestOptions).thenApply { it.parse() } - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Retrieves an assistant. */ override fun retrieve( params: BetaAssistantRetrieveParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("assistants", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val updateHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // get /assistants/{assistant_id} + withRawResponse().retrieve(params, requestOptions).thenApply { it.parse() } - /** Modifies an assistant. */ override fun update( params: BetaAssistantUpdateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("assistants", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync( - clientOptions, - params, - params.model().map { it.toString() }.orElse(null), - ) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { updateHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // post /assistants/{assistant_id} + withRawResponse().update(params, requestOptions).thenApply { it.parse() } - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) - - /** Returns a list of assistants. */ override fun list( params: BetaAssistantListParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("assistants") - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - .let { BetaAssistantListPageAsync.of(this, params, it) } - } - } - - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // get /assistants + withRawResponse().list(params, requestOptions).thenApply { it.parse() } - /** Delete an assistant. */ override fun delete( params: BetaAssistantDeleteParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments("assistants", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CompletableFuture = + // delete /assistants/{assistant_id} + withRawResponse().delete(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + AssistantServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: BetaAssistantCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("assistants") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaAssistantRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("assistants", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun update( + params: BetaAssistantUpdateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("assistants", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync( + clientOptions, + params, + params.model().map { it.toString() }.orElse(null), + ) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: BetaAssistantListParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("assistants") + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + BetaAssistantListPageAsync.of( + AssistantServiceAsyncImpl(clientOptions), + params, + it, + ) + } + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun delete( + params: BetaAssistantDeleteParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments("assistants", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/ThreadServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/ThreadServiceAsync.kt index ab22b9749..15910f840 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/ThreadServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/ThreadServiceAsync.kt @@ -4,8 +4,11 @@ package com.openai.services.async.beta +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions import com.openai.core.http.AsyncStreamResponse +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.StreamResponse import com.openai.models.AssistantStreamEvent import com.openai.models.BetaThreadCreateAndRunParams import com.openai.models.BetaThreadCreateParams @@ -21,6 +24,11 @@ import java.util.concurrent.CompletableFuture interface ThreadServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun runs(): RunServiceAsync fun messages(): MessageServiceAsync @@ -70,4 +78,88 @@ interface ThreadServiceAsync { params: BetaThreadCreateAndRunParams, requestOptions: RequestOptions = RequestOptions.none(), ): AsyncStreamResponse + + /** + * A view of [ThreadServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + fun runs(): RunServiceAsync.WithRawResponse + + fun messages(): MessageServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /threads`, but is otherwise the same as + * [ThreadServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BetaThreadCreateParams = BetaThreadCreateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /threads`, but is otherwise the same as + * [ThreadServiceAsync.create]. + */ + @MustBeClosed + fun create(requestOptions: RequestOptions): CompletableFuture> = + create(BetaThreadCreateParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /threads/{thread_id}`, but is otherwise the same as + * [ThreadServiceAsync.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaThreadRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /threads/{thread_id}`, but is otherwise the same as + * [ThreadServiceAsync.update]. + */ + @JvmOverloads + @MustBeClosed + fun update( + params: BetaThreadUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `delete /threads/{thread_id}`, but is otherwise the same + * as [ThreadServiceAsync.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: BetaThreadDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /threads/runs`, but is otherwise the same as + * [ThreadServiceAsync.createAndRun]. + */ + @JvmOverloads + @MustBeClosed + fun createAndRun( + params: BetaThreadCreateAndRunParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /threads/runs`, but is otherwise the same as + * [ThreadServiceAsync.createAndRunStreaming]. + */ + @JvmOverloads + @MustBeClosed + fun createAndRunStreaming( + params: BetaThreadCreateAndRunParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/ThreadServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/ThreadServiceAsyncImpl.kt index 5298e0955..220e3855e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/ThreadServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/ThreadServiceAsyncImpl.kt @@ -15,10 +15,12 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor import com.openai.core.http.StreamResponse +import com.openai.core.http.json import com.openai.core.http.map +import com.openai.core.http.parseable import com.openai.core.http.toAsync -import com.openai.core.json import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.AssistantStreamEvent @@ -44,208 +46,286 @@ class ThreadServiceAsyncImpl internal constructor(private val clientOptions: Cli private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: ThreadServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } private val runs: RunServiceAsync by lazy { RunServiceAsyncImpl(clientOptions) } private val messages: MessageServiceAsync by lazy { MessageServiceAsyncImpl(clientOptions) } + override fun withRawResponse(): ThreadServiceAsync.WithRawResponse = withRawResponse + override fun runs(): RunServiceAsync = runs override fun messages(): MessageServiceAsync = messages - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Create a thread. */ override fun create( params: BetaThreadCreateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // post /threads + withRawResponse().create(params, requestOptions).thenApply { it.parse() } - /** Retrieves a thread. */ override fun retrieve( params: BetaThreadRetrieveParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("threads", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val updateHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // get /threads/{thread_id} + withRawResponse().retrieve(params, requestOptions).thenApply { it.parse() } - /** Modifies a thread. */ override fun update( params: BetaThreadUpdateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { updateHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // post /threads/{thread_id} + withRawResponse().update(params, requestOptions).thenApply { it.parse() } - /** Delete a thread. */ override fun delete( params: BetaThreadDeleteParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments("threads", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val createAndRunHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // delete /threads/{thread_id} + withRawResponse().delete(params, requestOptions).thenApply { it.parse() } - /** Create a thread and run it in one request. */ override fun createAndRun( params: BetaThreadCreateAndRunParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads", "runs") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync( - clientOptions, - params, - params.model().map { it.toString() }.orElse(null), - ) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { createAndRunHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val createAndRunStreamingHandler: Handler> = - sseHandler(clientOptions.jsonMapper) - .mapJson(includeEventAndData = true) - .withErrorHandler(errorHandler) + ): CompletableFuture = + // post /threads/runs + withRawResponse().createAndRun(params, requestOptions).thenApply { it.parse() } - /** Create a thread and run it in one request. */ override fun createAndRunStreaming( params: BetaThreadCreateAndRunParams, requestOptions: RequestOptions, - ): AsyncStreamResponse { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads", "runs") - .putAllHeaders(DEFAULT_HEADERS) - .body( - json( - clientOptions.jsonMapper, - params - ._body() - .toBuilder() - .putAdditionalProperty("stream", JsonValue.from(true)) - .build(), + ): AsyncStreamResponse = + // post /threads/runs + withRawResponse() + .createAndRunStreaming(params, requestOptions) + .thenApply { it.parse() } + .toAsync(clientOptions.streamHandlerExecutor) + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ThreadServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val runs: RunServiceAsync.WithRawResponse by lazy { + RunServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val messages: MessageServiceAsync.WithRawResponse by lazy { + MessageServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun runs(): RunServiceAsync.WithRawResponse = runs + + override fun messages(): MessageServiceAsync.WithRawResponse = messages + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: BetaThreadCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("threads") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaThreadRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("threads", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun update( + params: BetaThreadUpdateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("threads", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun delete( + params: BetaThreadDeleteParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments("threads", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val createAndRunHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun createAndRun( + params: BetaThreadCreateAndRunParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("threads", "runs") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync( + clientOptions, + params, + params.model().map { it.toString() }.orElse(null), ) - ) - .build() - .prepareAsync( - clientOptions, - params, - params.model().map { it.toString() }.orElse(null), - ) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .let { createAndRunStreamingHandler.handle(it) } - .let { streamResponse -> - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - streamResponse.map { it.validate() } - } else { - streamResponse - } + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createAndRunHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } - .toAsync(clientOptions.streamHandlerExecutor) + } + } + + private val createAndRunStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper) + .mapJson(includeEventAndData = true) + .withErrorHandler(errorHandler) + + override fun createAndRunStreaming( + params: BetaThreadCreateAndRunParams, + requestOptions: RequestOptions, + ): CompletableFuture>> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("threads", "runs") + .putAllHeaders(DEFAULT_HEADERS) + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("stream", JsonValue.from(true)) + .build(), + ) + ) + .build() + .prepareAsync( + clientOptions, + params, + params.model().map { it.toString() }.orElse(null), + ) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .let { createAndRunStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } + } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/VectorStoreServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/VectorStoreServiceAsync.kt index 3ad93d7ef..689e51f52 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/VectorStoreServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/VectorStoreServiceAsync.kt @@ -4,7 +4,9 @@ package com.openai.services.async.beta +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.BetaVectorStoreCreateParams import com.openai.models.BetaVectorStoreDeleteParams import com.openai.models.BetaVectorStoreListPageAsync @@ -19,6 +21,11 @@ import java.util.concurrent.CompletableFuture interface VectorStoreServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun files(): FileServiceAsync fun fileBatches(): FileBatchServiceAsync @@ -61,4 +68,80 @@ interface VectorStoreServiceAsync { params: BetaVectorStoreDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** + * A view of [VectorStoreServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + fun files(): FileServiceAsync.WithRawResponse + + fun fileBatches(): FileBatchServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /vector_stores`, but is otherwise the same as + * [VectorStoreServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BetaVectorStoreCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /vector_stores/{vector_store_id}`, but is otherwise + * the same as [VectorStoreServiceAsync.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaVectorStoreRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /vector_stores/{vector_store_id}`, but is otherwise + * the same as [VectorStoreServiceAsync.update]. + */ + @JvmOverloads + @MustBeClosed + fun update( + params: BetaVectorStoreUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /vector_stores`, but is otherwise the same as + * [VectorStoreServiceAsync.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: BetaVectorStoreListParams = BetaVectorStoreListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /vector_stores`, but is otherwise the same as + * [VectorStoreServiceAsync.list]. + */ + @MustBeClosed + fun list( + requestOptions: RequestOptions + ): CompletableFuture> = + list(BetaVectorStoreListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /vector_stores/{vector_store_id}`, but is + * otherwise the same as [VectorStoreServiceAsync.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: BetaVectorStoreDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/VectorStoreServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/VectorStoreServiceAsyncImpl.kt index fdd0c8c6d..0953b151d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/VectorStoreServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/VectorStoreServiceAsyncImpl.kt @@ -11,7 +11,9 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.BetaVectorStoreCreateParams @@ -36,7 +38,9 @@ class VectorStoreServiceAsyncImpl internal constructor(private val clientOptions private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: VectorStoreServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } private val files: FileServiceAsync by lazy { FileServiceAsyncImpl(clientOptions) } @@ -44,152 +48,223 @@ class VectorStoreServiceAsyncImpl internal constructor(private val clientOptions FileBatchServiceAsyncImpl(clientOptions) } + override fun withRawResponse(): VectorStoreServiceAsync.WithRawResponse = withRawResponse + override fun files(): FileServiceAsync = files override fun fileBatches(): FileBatchServiceAsync = fileBatches - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Create a vector store. */ override fun create( params: BetaVectorStoreCreateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("vector_stores") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // post /vector_stores + withRawResponse().create(params, requestOptions).thenApply { it.parse() } - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Retrieves a vector store. */ override fun retrieve( params: BetaVectorStoreRetrieveParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("vector_stores", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val updateHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // get /vector_stores/{vector_store_id} + withRawResponse().retrieve(params, requestOptions).thenApply { it.parse() } - /** Modifies a vector store. */ override fun update( params: BetaVectorStoreUpdateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("vector_stores", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { updateHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + ): CompletableFuture = + // post /vector_stores/{vector_store_id} + withRawResponse().update(params, requestOptions).thenApply { it.parse() } - /** Returns a list of vector stores. */ override fun list( params: BetaVectorStoreListParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("vector_stores") - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - .let { BetaVectorStoreListPageAsync.of(this, params, it) } - } - } + ): CompletableFuture = + // get /vector_stores + withRawResponse().list(params, requestOptions).thenApply { it.parse() } - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Delete a vector store. */ override fun delete( params: BetaVectorStoreDeleteParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments("vector_stores", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CompletableFuture = + // delete /vector_stores/{vector_store_id} + withRawResponse().delete(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + VectorStoreServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val files: FileServiceAsync.WithRawResponse by lazy { + FileServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val fileBatches: FileBatchServiceAsync.WithRawResponse by lazy { + FileBatchServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun files(): FileServiceAsync.WithRawResponse = files + + override fun fileBatches(): FileBatchServiceAsync.WithRawResponse = fileBatches + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: BetaVectorStoreCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("vector_stores") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaVectorStoreRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("vector_stores", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun update( + params: BetaVectorStoreUpdateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("vector_stores", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: BetaVectorStoreListParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("vector_stores") + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + BetaVectorStoreListPageAsync.of( + VectorStoreServiceAsyncImpl(clientOptions), + params, + it, + ) + } + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun delete( + params: BetaVectorStoreDeleteParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments("vector_stores", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/MessageServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/MessageServiceAsync.kt index 77b8f2aef..43ce6619d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/MessageServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/MessageServiceAsync.kt @@ -4,7 +4,9 @@ package com.openai.services.async.beta.threads +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.BetaThreadMessageCreateParams import com.openai.models.BetaThreadMessageDeleteParams import com.openai.models.BetaThreadMessageListPageAsync @@ -17,6 +19,11 @@ import java.util.concurrent.CompletableFuture interface MessageServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** Create a message. */ @JvmOverloads fun create( @@ -51,4 +58,65 @@ interface MessageServiceAsync { params: BetaThreadMessageDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** + * A view of [MessageServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /threads/{thread_id}/messages`, but is otherwise + * the same as [MessageServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BetaThreadMessageCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /threads/{thread_id}/messages/{message_id}`, but is + * otherwise the same as [MessageServiceAsync.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaThreadMessageRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /threads/{thread_id}/messages/{message_id}`, but is + * otherwise the same as [MessageServiceAsync.update]. + */ + @JvmOverloads + @MustBeClosed + fun update( + params: BetaThreadMessageUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /threads/{thread_id}/messages`, but is otherwise the + * same as [MessageServiceAsync.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: BetaThreadMessageListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `delete /threads/{thread_id}/messages/{message_id}`, but + * is otherwise the same as [MessageServiceAsync.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: BetaThreadMessageDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/MessageServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/MessageServiceAsyncImpl.kt index 0d56382b3..e226a4691 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/MessageServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/MessageServiceAsyncImpl.kt @@ -11,7 +11,9 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.BetaThreadMessageCreateParams @@ -32,165 +34,226 @@ class MessageServiceAsyncImpl internal constructor(private val clientOptions: Cl private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: MessageServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun withRawResponse(): MessageServiceAsync.WithRawResponse = withRawResponse - /** Create a message. */ override fun create( params: BetaThreadMessageCreateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads", params.getPathParam(0), "messages") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // post /threads/{thread_id}/messages + withRawResponse().create(params, requestOptions).thenApply { it.parse() } - /** Retrieve a message. */ override fun retrieve( params: BetaThreadMessageRetrieveParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments( - "threads", - params.getPathParam(0), - "messages", - params.getPathParam(1), - ) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // get /threads/{thread_id}/messages/{message_id} + withRawResponse().retrieve(params, requestOptions).thenApply { it.parse() } - private val updateHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Modifies a message. */ override fun update( params: BetaThreadMessageUpdateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments( - "threads", - params.getPathParam(0), - "messages", - params.getPathParam(1), - ) - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { updateHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + ): CompletableFuture = + // post /threads/{thread_id}/messages/{message_id} + withRawResponse().update(params, requestOptions).thenApply { it.parse() } - /** Returns a list of messages for a given thread. */ override fun list( params: BetaThreadMessageListParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("threads", params.getPathParam(0), "messages") - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - .let { BetaThreadMessageListPageAsync.of(this, params, it) } - } - } + ): CompletableFuture = + // get /threads/{thread_id}/messages + withRawResponse().list(params, requestOptions).thenApply { it.parse() } - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Deletes a message. */ override fun delete( params: BetaThreadMessageDeleteParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments( - "threads", - params.getPathParam(0), - "messages", - params.getPathParam(1), - ) - .putAllHeaders(DEFAULT_HEADERS) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CompletableFuture = + // delete /threads/{thread_id}/messages/{message_id} + withRawResponse().delete(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + MessageServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: BetaThreadMessageCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("threads", params.getPathParam(0), "messages") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaThreadMessageRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments( + "threads", + params.getPathParam(0), + "messages", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun update( + params: BetaThreadMessageUpdateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments( + "threads", + params.getPathParam(0), + "messages", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: BetaThreadMessageListParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("threads", params.getPathParam(0), "messages") + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + BetaThreadMessageListPageAsync.of( + MessageServiceAsyncImpl(clientOptions), + params, + it, + ) + } + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun delete( + params: BetaThreadMessageDeleteParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments( + "threads", + params.getPathParam(0), + "messages", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/RunServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/RunServiceAsync.kt index 1edafe7be..044b7765e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/RunServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/RunServiceAsync.kt @@ -4,8 +4,11 @@ package com.openai.services.async.beta.threads +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions import com.openai.core.http.AsyncStreamResponse +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.StreamResponse import com.openai.models.AssistantStreamEvent import com.openai.models.BetaThreadRunCancelParams import com.openai.models.BetaThreadRunCreateParams @@ -20,6 +23,11 @@ import java.util.concurrent.CompletableFuture interface RunServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun steps(): StepServiceAsync /** Create a run. */ @@ -85,4 +93,100 @@ interface RunServiceAsync { params: BetaThreadRunSubmitToolOutputsParams, requestOptions: RequestOptions = RequestOptions.none(), ): AsyncStreamResponse + + /** A view of [RunServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun steps(): StepServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /threads/{thread_id}/runs`, but is otherwise the + * same as [RunServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BetaThreadRunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /threads/{thread_id}/runs`, but is otherwise the + * same as [RunServiceAsync.createStreaming]. + */ + @JvmOverloads + @MustBeClosed + fun createStreaming( + params: BetaThreadRunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> + + /** + * Returns a raw HTTP response for `get /threads/{thread_id}/runs/{run_id}`, but is + * otherwise the same as [RunServiceAsync.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaThreadRunRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /threads/{thread_id}/runs/{run_id}`, but is + * otherwise the same as [RunServiceAsync.update]. + */ + @JvmOverloads + @MustBeClosed + fun update( + params: BetaThreadRunUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /threads/{thread_id}/runs`, but is otherwise the + * same as [RunServiceAsync.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: BetaThreadRunListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /threads/{thread_id}/runs/{run_id}/cancel`, but is + * otherwise the same as [RunServiceAsync.cancel]. + */ + @JvmOverloads + @MustBeClosed + fun cancel( + params: BetaThreadRunCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post + * /threads/{thread_id}/runs/{run_id}/submit_tool_outputs`, but is otherwise the same as + * [RunServiceAsync.submitToolOutputs]. + */ + @JvmOverloads + @MustBeClosed + fun submitToolOutputs( + params: BetaThreadRunSubmitToolOutputsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post + * /threads/{thread_id}/runs/{run_id}/submit_tool_outputs`, but is otherwise the same as + * [RunServiceAsync.submitToolOutputsStreaming]. + */ + @JvmOverloads + @MustBeClosed + fun submitToolOutputsStreaming( + params: BetaThreadRunSubmitToolOutputsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/RunServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/RunServiceAsyncImpl.kt index 23d204c12..f722c0f2d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/RunServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/RunServiceAsyncImpl.kt @@ -15,10 +15,12 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor import com.openai.core.http.StreamResponse +import com.openai.core.http.json import com.openai.core.http.map +import com.openai.core.http.parseable import com.openai.core.http.toAsync -import com.openai.core.json import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.AssistantStreamEvent @@ -42,303 +44,404 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: RunServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } private val steps: StepServiceAsync by lazy { StepServiceAsyncImpl(clientOptions) } - override fun steps(): StepServiceAsync = steps + override fun withRawResponse(): RunServiceAsync.WithRawResponse = withRawResponse - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun steps(): StepServiceAsync = steps - /** Create a run. */ override fun create( params: BetaThreadRunCreateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads", params.getPathParam(0), "runs") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync( - clientOptions, - params, - params.model().map { it.toString() }.orElse(null), - ) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val createStreamingHandler: Handler> = - sseHandler(clientOptions.jsonMapper) - .mapJson(includeEventAndData = true) - .withErrorHandler(errorHandler) + ): CompletableFuture = + // post /threads/{thread_id}/runs + withRawResponse().create(params, requestOptions).thenApply { it.parse() } - /** Create a run. */ override fun createStreaming( params: BetaThreadRunCreateParams, requestOptions: RequestOptions, - ): AsyncStreamResponse { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads", params.getPathParam(0), "runs") - .putAllHeaders(DEFAULT_HEADERS) - .body( - json( - clientOptions.jsonMapper, - params - ._body() - .toBuilder() - .putAdditionalProperty("stream", JsonValue.from(true)) - .build(), - ) - ) - .build() - .prepareAsync( - clientOptions, - params, - params.model().map { it.toString() }.orElse(null), - ) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .let { createStreamingHandler.handle(it) } - .let { streamResponse -> - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - streamResponse.map { it.validate() } - } else { - streamResponse - } - } - } + ): AsyncStreamResponse = + // post /threads/{thread_id}/runs + withRawResponse() + .createStreaming(params, requestOptions) + .thenApply { it.parse() } .toAsync(clientOptions.streamHandlerExecutor) - } - - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - /** Retrieves a run. */ override fun retrieve( params: BetaThreadRunRetrieveParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("threads", params.getPathParam(0), "runs", params.getPathParam(1)) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // get /threads/{thread_id}/runs/{run_id} + withRawResponse().retrieve(params, requestOptions).thenApply { it.parse() } - private val updateHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Modifies a run. */ override fun update( params: BetaThreadRunUpdateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads", params.getPathParam(0), "runs", params.getPathParam(1)) - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { updateHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + ): CompletableFuture = + // post /threads/{thread_id}/runs/{run_id} + withRawResponse().update(params, requestOptions).thenApply { it.parse() } - /** Returns a list of runs belonging to a thread. */ override fun list( params: BetaThreadRunListParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("threads", params.getPathParam(0), "runs") - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - .let { BetaThreadRunListPageAsync.of(this, params, it) } - } - } + ): CompletableFuture = + // get /threads/{thread_id}/runs + withRawResponse().list(params, requestOptions).thenApply { it.parse() } - private val cancelHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Cancels a run that is `in_progress`. */ override fun cancel( params: BetaThreadRunCancelParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments( - "threads", - params.getPathParam(0), - "runs", - params.getPathParam(1), - "cancel", - ) - .putAllHeaders(DEFAULT_HEADERS) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { cancelHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val submitToolOutputsHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // post /threads/{thread_id}/runs/{run_id}/cancel + withRawResponse().cancel(params, requestOptions).thenApply { it.parse() } - /** - * When a run has the `status: "requires_action"` and `required_action.type` is - * `submit_tool_outputs`, this endpoint can be used to submit the outputs from the tool calls - * once they're all completed. All outputs must be submitted in a single request. - */ override fun submitToolOutputs( params: BetaThreadRunSubmitToolOutputsParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments( - "threads", - params.getPathParam(0), - "runs", - params.getPathParam(1), - "submit_tool_outputs", - ) - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { submitToolOutputsHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // post /threads/{thread_id}/runs/{run_id}/submit_tool_outputs + withRawResponse().submitToolOutputs(params, requestOptions).thenApply { it.parse() } - private val submitToolOutputsStreamingHandler: Handler> = - sseHandler(clientOptions.jsonMapper) - .mapJson(includeEventAndData = true) - .withErrorHandler(errorHandler) - - /** - * When a run has the `status: "requires_action"` and `required_action.type` is - * `submit_tool_outputs`, this endpoint can be used to submit the outputs from the tool calls - * once they're all completed. All outputs must be submitted in a single request. - */ override fun submitToolOutputsStreaming( params: BetaThreadRunSubmitToolOutputsParams, requestOptions: RequestOptions, - ): AsyncStreamResponse { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments( - "threads", - params.getPathParam(0), - "runs", - params.getPathParam(1), - "submit_tool_outputs", - ) - .putAllHeaders(DEFAULT_HEADERS) - .body( - json( - clientOptions.jsonMapper, - params - ._body() - .toBuilder() - .putAdditionalProperty("stream", JsonValue.from(true)) - .build(), + ): AsyncStreamResponse = + // post /threads/{thread_id}/runs/{run_id}/submit_tool_outputs + withRawResponse() + .submitToolOutputsStreaming(params, requestOptions) + .thenApply { it.parse() } + .toAsync(clientOptions.streamHandlerExecutor) + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + RunServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val steps: StepServiceAsync.WithRawResponse by lazy { + StepServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun steps(): StepServiceAsync.WithRawResponse = steps + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: BetaThreadRunCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("threads", params.getPathParam(0), "runs") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync( + clientOptions, + params, + params.model().map { it.toString() }.orElse(null), ) - ) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .let { submitToolOutputsStreamingHandler.handle(it) } - .let { streamResponse -> - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - streamResponse.map { it.validate() } - } else { - streamResponse - } + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } - .toAsync(clientOptions.streamHandlerExecutor) + } + } + + private val createStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper) + .mapJson(includeEventAndData = true) + .withErrorHandler(errorHandler) + + override fun createStreaming( + params: BetaThreadRunCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture>> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("threads", params.getPathParam(0), "runs") + .putAllHeaders(DEFAULT_HEADERS) + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("stream", JsonValue.from(true)) + .build(), + ) + ) + .build() + .prepareAsync( + clientOptions, + params, + params.model().map { it.toString() }.orElse(null), + ) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .let { createStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaThreadRunRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments( + "threads", + params.getPathParam(0), + "runs", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun update( + params: BetaThreadRunUpdateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments( + "threads", + params.getPathParam(0), + "runs", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: BetaThreadRunListParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("threads", params.getPathParam(0), "runs") + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + BetaThreadRunListPageAsync.of( + RunServiceAsyncImpl(clientOptions), + params, + it, + ) + } + } + } + } + + private val cancelHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun cancel( + params: BetaThreadRunCancelParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments( + "threads", + params.getPathParam(0), + "runs", + params.getPathParam(1), + "cancel", + ) + .putAllHeaders(DEFAULT_HEADERS) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { cancelHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val submitToolOutputsHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun submitToolOutputs( + params: BetaThreadRunSubmitToolOutputsParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments( + "threads", + params.getPathParam(0), + "runs", + params.getPathParam(1), + "submit_tool_outputs", + ) + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { submitToolOutputsHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val submitToolOutputsStreamingHandler: + Handler> = + sseHandler(clientOptions.jsonMapper) + .mapJson(includeEventAndData = true) + .withErrorHandler(errorHandler) + + override fun submitToolOutputsStreaming( + params: BetaThreadRunSubmitToolOutputsParams, + requestOptions: RequestOptions, + ): CompletableFuture>> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments( + "threads", + params.getPathParam(0), + "runs", + params.getPathParam(1), + "submit_tool_outputs", + ) + .putAllHeaders(DEFAULT_HEADERS) + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("stream", JsonValue.from(true)) + .build(), + ) + ) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .let { submitToolOutputsStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } + } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/runs/StepServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/runs/StepServiceAsync.kt index 82eac83cf..870054ce4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/runs/StepServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/runs/StepServiceAsync.kt @@ -4,7 +4,9 @@ package com.openai.services.async.beta.threads.runs +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.BetaThreadRunStepListPageAsync import com.openai.models.BetaThreadRunStepListParams import com.openai.models.BetaThreadRunStepRetrieveParams @@ -13,6 +15,11 @@ import java.util.concurrent.CompletableFuture interface StepServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** Retrieves a run step. */ @JvmOverloads fun retrieve( @@ -26,4 +33,30 @@ interface StepServiceAsync { params: BetaThreadRunStepListParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** A view of [StepServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `get /threads/{thread_id}/runs/{run_id}/steps/{step_id}`, + * but is otherwise the same as [StepServiceAsync.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaThreadRunStepRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /threads/{thread_id}/runs/{run_id}/steps`, but is + * otherwise the same as [StepServiceAsync.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: BetaThreadRunStepListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/runs/StepServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/runs/StepServiceAsyncImpl.kt index 31f9e62f1..5f7b41e99 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/runs/StepServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/runs/StepServiceAsyncImpl.kt @@ -11,6 +11,8 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.BetaThreadRunStepListPageAsync @@ -27,76 +29,110 @@ class StepServiceAsyncImpl internal constructor(private val clientOptions: Clien private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: StepServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun withRawResponse(): StepServiceAsync.WithRawResponse = withRawResponse - /** Retrieves a run step. */ override fun retrieve( params: BetaThreadRunStepRetrieveParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments( - "threads", - params.getPathParam(0), - "runs", - params.getPathParam(1), - "steps", - params.getPathParam(2), - ) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // get /threads/{thread_id}/runs/{run_id}/steps/{step_id} + withRawResponse().retrieve(params, requestOptions).thenApply { it.parse() } - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) - - /** Returns a list of run steps belonging to a run. */ override fun list( params: BetaThreadRunStepListParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments( - "threads", - params.getPathParam(0), - "runs", - params.getPathParam(1), - "steps", - ) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CompletableFuture = + // get /threads/{thread_id}/runs/{run_id}/steps + withRawResponse().list(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + StepServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaThreadRunStepRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments( + "threads", + params.getPathParam(0), + "runs", + params.getPathParam(1), + "steps", + params.getPathParam(2), + ) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: BetaThreadRunStepListParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments( + "threads", + params.getPathParam(0), + "runs", + params.getPathParam(1), + "steps", + ) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + BetaThreadRunStepListPageAsync.of( + StepServiceAsyncImpl(clientOptions), + params, + it, + ) + } } - .let { BetaThreadRunStepListPageAsync.of(this, params, it) } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileBatchServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileBatchServiceAsync.kt index 5359ddd1b..37c81b4a2 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileBatchServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileBatchServiceAsync.kt @@ -4,7 +4,9 @@ package com.openai.services.async.beta.vectorStores +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.BetaVectorStoreFileBatchCancelParams import com.openai.models.BetaVectorStoreFileBatchCreateParams import com.openai.models.BetaVectorStoreFileBatchListFilesPageAsync @@ -15,6 +17,11 @@ import java.util.concurrent.CompletableFuture interface FileBatchServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** Create a vector store file batch. */ @JvmOverloads fun create( @@ -45,4 +52,57 @@ interface FileBatchServiceAsync { params: BetaVectorStoreFileBatchListFilesParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** + * A view of [FileBatchServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /vector_stores/{vector_store_id}/file_batches`, but + * is otherwise the same as [FileBatchServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BetaVectorStoreFileBatchCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get + * /vector_stores/{vector_store_id}/file_batches/{batch_id}`, but is otherwise the same as + * [FileBatchServiceAsync.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaVectorStoreFileBatchRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post + * /vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel`, but is otherwise the + * same as [FileBatchServiceAsync.cancel]. + */ + @JvmOverloads + @MustBeClosed + fun cancel( + params: BetaVectorStoreFileBatchCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get + * /vector_stores/{vector_store_id}/file_batches/{batch_id}/files`, but is otherwise the + * same as [FileBatchServiceAsync.listFiles]. + */ + @JvmOverloads + @MustBeClosed + fun listFiles( + params: BetaVectorStoreFileBatchListFilesParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileBatchServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileBatchServiceAsyncImpl.kt index 80d0187a5..921d0ff15 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileBatchServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileBatchServiceAsyncImpl.kt @@ -11,7 +11,9 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.BetaVectorStoreFileBatchCancelParams @@ -30,141 +32,195 @@ class FileBatchServiceAsyncImpl internal constructor(private val clientOptions: private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: FileBatchServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun withRawResponse(): FileBatchServiceAsync.WithRawResponse = withRawResponse - /** Create a vector store file batch. */ override fun create( params: BetaVectorStoreFileBatchCreateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("vector_stores", params.getPathParam(0), "file_batches") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // post /vector_stores/{vector_store_id}/file_batches + withRawResponse().create(params, requestOptions).thenApply { it.parse() } - /** Retrieves a vector store file batch. */ override fun retrieve( params: BetaVectorStoreFileBatchRetrieveParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments( - "vector_stores", - params.getPathParam(0), - "file_batches", - params.getPathParam(1), - ) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // get /vector_stores/{vector_store_id}/file_batches/{batch_id} + withRawResponse().retrieve(params, requestOptions).thenApply { it.parse() } - private val cancelHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Cancel a vector store file batch. This attempts to cancel the processing of files in this - * batch as soon as possible. - */ override fun cancel( params: BetaVectorStoreFileBatchCancelParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments( - "vector_stores", - params.getPathParam(0), - "file_batches", - params.getPathParam(1), - "cancel", - ) - .putAllHeaders(DEFAULT_HEADERS) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { cancelHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val listFilesHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + ): CompletableFuture = + // post /vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel + withRawResponse().cancel(params, requestOptions).thenApply { it.parse() } - /** Returns a list of vector store files in a batch. */ override fun listFiles( params: BetaVectorStoreFileBatchListFilesParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments( - "vector_stores", - params.getPathParam(0), - "file_batches", - params.getPathParam(1), - "files", + ): CompletableFuture = + // get /vector_stores/{vector_store_id}/file_batches/{batch_id}/files + withRawResponse().listFiles(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + FileBatchServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun create( + params: BetaVectorStoreFileBatchCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("vector_stores", params.getPathParam(0), "file_batches") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaVectorStoreFileBatchRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments( + "vector_stores", + params.getPathParam(0), + "file_batches", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val cancelHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun cancel( + params: BetaVectorStoreFileBatchCancelParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments( + "vector_stores", + params.getPathParam(0), + "file_batches", + params.getPathParam(1), + "cancel", + ) + .putAllHeaders(DEFAULT_HEADERS) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { cancelHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val listFilesHandler: Handler = + jsonHandler( + clientOptions.jsonMapper ) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { listFilesHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + .withErrorHandler(errorHandler) + + override fun listFiles( + params: BetaVectorStoreFileBatchListFilesParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments( + "vector_stores", + params.getPathParam(0), + "file_batches", + params.getPathParam(1), + "files", + ) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { listFilesHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + BetaVectorStoreFileBatchListFilesPageAsync.of( + FileBatchServiceAsyncImpl(clientOptions), + params, + it, + ) + } } - .let { BetaVectorStoreFileBatchListFilesPageAsync.of(this, params, it) } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileServiceAsync.kt index e1b090b4b..1d6e61a82 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileServiceAsync.kt @@ -4,7 +4,9 @@ package com.openai.services.async.beta.vectorStores +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.BetaVectorStoreFileCreateParams import com.openai.models.BetaVectorStoreFileDeleteParams import com.openai.models.BetaVectorStoreFileListPageAsync @@ -16,6 +18,11 @@ import java.util.concurrent.CompletableFuture interface FileServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** * Create a vector store file by attaching a * [File](https://platform.openai.com/docs/api-reference/files) to a @@ -51,4 +58,53 @@ interface FileServiceAsync { params: BetaVectorStoreFileDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** A view of [FileServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /vector_stores/{vector_store_id}/files`, but is + * otherwise the same as [FileServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BetaVectorStoreFileCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /vector_stores/{vector_store_id}/files/{file_id}`, + * but is otherwise the same as [FileServiceAsync.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaVectorStoreFileRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /vector_stores/{vector_store_id}/files`, but is + * otherwise the same as [FileServiceAsync.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: BetaVectorStoreFileListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `delete + * /vector_stores/{vector_store_id}/files/{file_id}`, but is otherwise the same as + * [FileServiceAsync.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: BetaVectorStoreFileDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileServiceAsyncImpl.kt index 06f208385..bfaf80e7e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/vectorStores/FileServiceAsyncImpl.kt @@ -11,7 +11,9 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.BetaVectorStoreFileCreateParams @@ -31,139 +33,184 @@ class FileServiceAsyncImpl internal constructor(private val clientOptions: Clien private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: FileServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun withRawResponse(): FileServiceAsync.WithRawResponse = withRawResponse - /** - * Create a vector store file by attaching a - * [File](https://platform.openai.com/docs/api-reference/files) to a - * [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object). - */ override fun create( params: BetaVectorStoreFileCreateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("vector_stores", params.getPathParam(0), "files") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // post /vector_stores/{vector_store_id}/files + withRawResponse().create(params, requestOptions).thenApply { it.parse() } - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Retrieves a vector store file. */ override fun retrieve( params: BetaVectorStoreFileRetrieveParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments( - "vector_stores", - params.getPathParam(0), - "files", - params.getPathParam(1), - ) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // get /vector_stores/{vector_store_id}/files/{file_id} + withRawResponse().retrieve(params, requestOptions).thenApply { it.parse() } - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) - - /** Returns a list of vector store files. */ override fun list( params: BetaVectorStoreFileListParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("vector_stores", params.getPathParam(0), "files") - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - .let { BetaVectorStoreFileListPageAsync.of(this, params, it) } - } - } + ): CompletableFuture = + // get /vector_stores/{vector_store_id}/files + withRawResponse().list(params, requestOptions).thenApply { it.parse() } - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Delete a vector store file. This will remove the file from the vector store but the file - * itself will not be deleted. To delete the file, use the - * [delete file](https://platform.openai.com/docs/api-reference/files/delete) endpoint. - */ override fun delete( params: BetaVectorStoreFileDeleteParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments( - "vector_stores", - params.getPathParam(0), - "files", - params.getPathParam(1), - ) - .putAllHeaders(DEFAULT_HEADERS) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CompletableFuture = + // delete /vector_stores/{vector_store_id}/files/{file_id} + withRawResponse().delete(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + FileServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: BetaVectorStoreFileCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("vector_stores", params.getPathParam(0), "files") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaVectorStoreFileRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments( + "vector_stores", + params.getPathParam(0), + "files", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: BetaVectorStoreFileListParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("vector_stores", params.getPathParam(0), "files") + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + BetaVectorStoreFileListPageAsync.of( + FileServiceAsyncImpl(clientOptions), + params, + it, + ) + } + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun delete( + params: BetaVectorStoreFileDeleteParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments( + "vector_stores", + params.getPathParam(0), + "files", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/chat/CompletionServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/chat/CompletionServiceAsync.kt index 0d499a9a3..90c7da22a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/chat/CompletionServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/chat/CompletionServiceAsync.kt @@ -4,8 +4,11 @@ package com.openai.services.async.chat +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions import com.openai.core.http.AsyncStreamResponse +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.StreamResponse import com.openai.models.ChatCompletion import com.openai.models.ChatCompletionChunk import com.openai.models.ChatCompletionCreateParams @@ -18,6 +21,11 @@ import java.util.concurrent.CompletableFuture interface CompletionServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun messages(): MessageServiceAsync /** @@ -84,4 +92,68 @@ interface CompletionServiceAsync { params: ChatCompletionDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** + * A view of [CompletionServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + fun messages(): MessageServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /chat/completions`, but is otherwise the same as + * [CompletionServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: ChatCompletionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /chat/completions`, but is otherwise the same as + * [CompletionServiceAsync.createStreaming]. + */ + @JvmOverloads + @MustBeClosed + fun createStreaming( + params: ChatCompletionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> + + /** + * Returns a raw HTTP response for `get /chat/completions/{completion_id}`, but is otherwise + * the same as [CompletionServiceAsync.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: ChatCompletionRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /chat/completions/{completion_id}`, but is + * otherwise the same as [CompletionServiceAsync.update]. + */ + @JvmOverloads + @MustBeClosed + fun update( + params: ChatCompletionUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `delete /chat/completions/{completion_id}`, but is + * otherwise the same as [CompletionServiceAsync.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: ChatCompletionDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/chat/CompletionServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/chat/CompletionServiceAsyncImpl.kt index b5e0b0e3f..e8f7d81d3 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/chat/CompletionServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/chat/CompletionServiceAsyncImpl.kt @@ -14,10 +14,12 @@ import com.openai.core.http.AsyncStreamResponse import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor import com.openai.core.http.StreamResponse +import com.openai.core.http.json import com.openai.core.http.map +import com.openai.core.http.parseable import com.openai.core.http.toAsync -import com.openai.core.json import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.ChatCompletion @@ -34,192 +36,226 @@ import java.util.concurrent.CompletableFuture class CompletionServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : CompletionServiceAsync { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: CompletionServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } private val messages: MessageServiceAsync by lazy { MessageServiceAsyncImpl(clientOptions) } + override fun withRawResponse(): CompletionServiceAsync.WithRawResponse = withRawResponse + override fun messages(): MessageServiceAsync = messages - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Creates a model response for the given chat conversation. Learn more in the - * [text generation](https://platform.openai.com/docs/guides/text-generation), - * [vision](https://platform.openai.com/docs/guides/vision), and - * [audio](https://platform.openai.com/docs/guides/audio) guides. - * - * Parameter support can differ depending on the model used to generate the response, - * particularly for newer reasoning models. Parameters that are only supported for reasoning - * models are noted below. For the current state of unsupported parameters in reasoning models, - * [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). - */ override fun create( params: ChatCompletionCreateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("chat", "completions") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, params.model().toString()) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // post /chat/completions + withRawResponse().create(params, requestOptions).thenApply { it.parse() } - private val createStreamingHandler: Handler> = - sseHandler(clientOptions.jsonMapper) - .mapJson() - .withErrorHandler(errorHandler) - - /** - * Creates a model response for the given chat conversation. Learn more in the - * [text generation](https://platform.openai.com/docs/guides/text-generation), - * [vision](https://platform.openai.com/docs/guides/vision), and - * [audio](https://platform.openai.com/docs/guides/audio) guides. - * - * Parameter support can differ depending on the model used to generate the response, - * particularly for newer reasoning models. Parameters that are only supported for reasoning - * models are noted below. For the current state of unsupported parameters in reasoning models, - * [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). - */ override fun createStreaming( params: ChatCompletionCreateParams, requestOptions: RequestOptions, - ): AsyncStreamResponse { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("chat", "completions") - .body( - json( - clientOptions.jsonMapper, - params - ._body() - .toBuilder() - .putAdditionalProperty("stream", JsonValue.from(true)) - .build(), - ) - ) - .build() - .prepareAsync(clientOptions, params, params.model().toString()) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .let { createStreamingHandler.handle(it) } - .let { streamResponse -> - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - streamResponse.map { it.validate() } - } else { - streamResponse - } - } - } + ): AsyncStreamResponse = + // post /chat/completions + withRawResponse() + .createStreaming(params, requestOptions) + .thenApply { it.parse() } .toAsync(clientOptions.streamHandlerExecutor) - } - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Get a stored chat completion. Only chat completions that have been created with the `store` - * parameter set to `true` will be returned. - */ override fun retrieve( params: ChatCompletionRetrieveParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("chat", "completions", params.getPathParam(0)) - .build() - .prepareAsync(clientOptions, params, null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val updateHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // get /chat/completions/{completion_id} + withRawResponse().retrieve(params, requestOptions).thenApply { it.parse() } - /** - * Modify a stored chat completion. Only chat completions that have been created with the - * `store` parameter set to `true` can be modified. Currently, the only supported modification - * is to update the `metadata` field. - */ override fun update( params: ChatCompletionUpdateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("chat", "completions", params.getPathParam(0)) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { updateHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // post /chat/completions/{completion_id} + withRawResponse().update(params, requestOptions).thenApply { it.parse() } - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Delete a stored chat completion. Only chat completions that have been created with the - * `store` parameter set to `true` can be deleted. - */ override fun delete( params: ChatCompletionDeleteParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments("chat", "completions", params.getPathParam(0)) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepareAsync(clientOptions, params, null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CompletableFuture = + // delete /chat/completions/{completion_id} + withRawResponse().delete(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CompletionServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val messages: MessageServiceAsync.WithRawResponse by lazy { + MessageServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun messages(): MessageServiceAsync.WithRawResponse = messages + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: ChatCompletionCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("chat", "completions") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val createStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper) + .mapJson() + .withErrorHandler(errorHandler) + + override fun createStreaming( + params: ChatCompletionCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture>> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("chat", "completions") + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("stream", JsonValue.from(true)) + .build(), + ) + ) + .build() + .prepareAsync(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .let { createStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: ChatCompletionRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("chat", "completions", params.getPathParam(0)) + .build() + .prepareAsync(clientOptions, params, null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun update( + params: ChatCompletionUpdateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("chat", "completions", params.getPathParam(0)) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun delete( + params: ChatCompletionDeleteParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments("chat", "completions", params.getPathParam(0)) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params, null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/chat/completions/MessageServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/chat/completions/MessageServiceAsync.kt index b3f803edc..9c4d05b07 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/chat/completions/MessageServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/chat/completions/MessageServiceAsync.kt @@ -4,13 +4,20 @@ package com.openai.services.async.chat.completions +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.ChatCompletionMessageListPageAsync import com.openai.models.ChatCompletionMessageListParams import java.util.concurrent.CompletableFuture interface MessageServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** * Get the messages in a stored chat completion. Only chat completions that have been created * with the `store` parameter set to `true` will be returned. @@ -20,4 +27,21 @@ interface MessageServiceAsync { params: ChatCompletionMessageListParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** + * A view of [MessageServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `get /chat/completions/{completion_id}/messages`, but is + * otherwise the same as [MessageServiceAsync.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: ChatCompletionMessageListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/chat/completions/MessageServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/chat/completions/MessageServiceAsyncImpl.kt index 21fb1d642..f2fc3b665 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/chat/completions/MessageServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/chat/completions/MessageServiceAsyncImpl.kt @@ -10,6 +10,8 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.ChatCompletionMessageListPageAsync @@ -19,37 +21,59 @@ import java.util.concurrent.CompletableFuture class MessageServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : MessageServiceAsync { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: MessageServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + override fun withRawResponse(): MessageServiceAsync.WithRawResponse = withRawResponse - /** - * Get the messages in a stored chat completion. Only chat completions that have been created - * with the `store` parameter set to `true` will be returned. - */ override fun list( params: ChatCompletionMessageListParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("chat", "completions", params.getPathParam(0), "messages") - .build() - .prepareAsync(clientOptions, params, null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CompletableFuture = + // get /chat/completions/{completion_id}/messages + withRawResponse().list(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + MessageServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: ChatCompletionMessageListParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("chat", "completions", params.getPathParam(0), "messages") + .build() + .prepareAsync(clientOptions, params, null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + ChatCompletionMessageListPageAsync.of( + MessageServiceAsyncImpl(clientOptions), + params, + it, + ) + } } - .let { ChatCompletionMessageListPageAsync.of(this, params, it) } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/JobServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/JobServiceAsync.kt index 360d1f3ed..2cf20c910 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/JobServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/JobServiceAsync.kt @@ -4,7 +4,9 @@ package com.openai.services.async.fineTuning +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.FineTuningJob import com.openai.models.FineTuningJobCancelParams import com.openai.models.FineTuningJobCreateParams @@ -18,6 +20,11 @@ import java.util.concurrent.CompletableFuture interface JobServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun checkpoints(): CheckpointServiceAsync /** @@ -70,4 +77,75 @@ interface JobServiceAsync { params: FineTuningJobListEventsParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** A view of [JobServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun checkpoints(): CheckpointServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /fine_tuning/jobs`, but is otherwise the same as + * [JobServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: FineTuningJobCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /fine_tuning/jobs/{fine_tuning_job_id}`, but is + * otherwise the same as [JobServiceAsync.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: FineTuningJobRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /fine_tuning/jobs`, but is otherwise the same as + * [JobServiceAsync.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: FineTuningJobListParams = FineTuningJobListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /fine_tuning/jobs`, but is otherwise the same as + * [JobServiceAsync.list]. + */ + @MustBeClosed + fun list( + requestOptions: RequestOptions + ): CompletableFuture> = + list(FineTuningJobListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /fine_tuning/jobs/{fine_tuning_job_id}/cancel`, but + * is otherwise the same as [JobServiceAsync.cancel]. + */ + @JvmOverloads + @MustBeClosed + fun cancel( + params: FineTuningJobCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /fine_tuning/jobs/{fine_tuning_job_id}/events`, but + * is otherwise the same as [JobServiceAsync.listEvents]. + */ + @JvmOverloads + @MustBeClosed + fun listEvents( + params: FineTuningJobListEventsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/JobServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/JobServiceAsyncImpl.kt index 2347d03fb..57c1adf6e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/JobServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/JobServiceAsyncImpl.kt @@ -10,7 +10,9 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.FineTuningJob @@ -28,164 +30,225 @@ import java.util.concurrent.CompletableFuture class JobServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : JobServiceAsync { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: JobServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } private val checkpoints: CheckpointServiceAsync by lazy { CheckpointServiceAsyncImpl(clientOptions) } + override fun withRawResponse(): JobServiceAsync.WithRawResponse = withRawResponse + override fun checkpoints(): CheckpointServiceAsync = checkpoints - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Creates a fine-tuning job which begins the process of creating a new model from a given - * dataset. - * - * Response includes details of the enqueued job including job status and the name of the - * fine-tuned models once complete. - * - * [Learn more about fine-tuning](https://platform.openai.com/docs/guides/fine-tuning) - */ override fun create( params: FineTuningJobCreateParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("fine_tuning", "jobs") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepareAsync(clientOptions, params, params.model().toString()) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // post /fine_tuning/jobs + withRawResponse().create(params, requestOptions).thenApply { it.parse() } - /** - * Get info about a fine-tuning job. - * - * [Learn more about fine-tuning](https://platform.openai.com/docs/guides/fine-tuning) - */ override fun retrieve( params: FineTuningJobRetrieveParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("fine_tuning", "jobs", params.getPathParam(0)) - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } + ): CompletableFuture = + // get /fine_tuning/jobs/{fine_tuning_job_id} + withRawResponse().retrieve(params, requestOptions).thenApply { it.parse() } - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) - - /** List your organization's fine-tuning jobs */ override fun list( params: FineTuningJobListParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("fine_tuning", "jobs") - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - .let { FineTuningJobListPageAsync.of(this, params, it) } - } - } - - private val cancelHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): CompletableFuture = + // get /fine_tuning/jobs + withRawResponse().list(params, requestOptions).thenApply { it.parse() } - /** Immediately cancel a fine-tune job. */ override fun cancel( params: FineTuningJobCancelParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("fine_tuning", "jobs", params.getPathParam(0), "cancel") - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { cancelHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - } - - private val listEventsHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + ): CompletableFuture = + // post /fine_tuning/jobs/{fine_tuning_job_id}/cancel + withRawResponse().cancel(params, requestOptions).thenApply { it.parse() } - /** Get status updates for a fine-tuning job. */ override fun listEvents( params: FineTuningJobListEventsParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("fine_tuning", "jobs", params.getPathParam(0), "events") - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { listEventsHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CompletableFuture = + // get /fine_tuning/jobs/{fine_tuning_job_id}/events + withRawResponse().listEvents(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + JobServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val checkpoints: CheckpointServiceAsync.WithRawResponse by lazy { + CheckpointServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun checkpoints(): CheckpointServiceAsync.WithRawResponse = checkpoints + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: FineTuningJobCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("fine_tuning", "jobs") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: FineTuningJobRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("fine_tuning", "jobs", params.getPathParam(0)) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: FineTuningJobListParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("fine_tuning", "jobs") + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + FineTuningJobListPageAsync.of( + JobServiceAsyncImpl(clientOptions), + params, + it, + ) + } + } + } + } + + private val cancelHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun cancel( + params: FineTuningJobCancelParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("fine_tuning", "jobs", params.getPathParam(0), "cancel") + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { cancelHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val listEventsHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun listEvents( + params: FineTuningJobListEventsParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("fine_tuning", "jobs", params.getPathParam(0), "events") + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { listEventsHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + FineTuningJobListEventsPageAsync.of( + JobServiceAsyncImpl(clientOptions), + params, + it, + ) + } } - .let { FineTuningJobListEventsPageAsync.of(this, params, it) } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/jobs/CheckpointServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/jobs/CheckpointServiceAsync.kt index 5d08d2323..71a532860 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/jobs/CheckpointServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/jobs/CheckpointServiceAsync.kt @@ -4,17 +4,42 @@ package com.openai.services.async.fineTuning.jobs +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.FineTuningJobCheckpointListPageAsync import com.openai.models.FineTuningJobCheckpointListParams import java.util.concurrent.CompletableFuture interface CheckpointServiceAsync { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** List checkpoints for a fine-tuning job. */ @JvmOverloads fun list( params: FineTuningJobCheckpointListParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** + * A view of [CheckpointServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `get /fine_tuning/jobs/{fine_tuning_job_id}/checkpoints`, + * but is otherwise the same as [CheckpointServiceAsync.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: FineTuningJobCheckpointListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/jobs/CheckpointServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/jobs/CheckpointServiceAsyncImpl.kt index a59bae18a..f6a5ba896 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/jobs/CheckpointServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/fineTuning/jobs/CheckpointServiceAsyncImpl.kt @@ -10,6 +10,8 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.parseable import com.openai.core.prepareAsync import com.openai.errors.OpenAIError import com.openai.models.FineTuningJobCheckpointListPageAsync @@ -19,34 +21,59 @@ import java.util.concurrent.CompletableFuture class CheckpointServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : CheckpointServiceAsync { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: CheckpointServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + override fun withRawResponse(): CheckpointServiceAsync.WithRawResponse = withRawResponse - /** List checkpoints for a fine-tuning job. */ override fun list( params: FineTuningJobCheckpointListParams, requestOptions: RequestOptions, - ): CompletableFuture { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("fine_tuning", "jobs", params.getPathParam(0), "checkpoints") - .build() - .prepareAsync(clientOptions, params, deploymentModel = null) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CompletableFuture = + // get /fine_tuning/jobs/{fine_tuning_job_id}/checkpoints + withRawResponse().list(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CheckpointServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: FineTuningJobCheckpointListParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("fine_tuning", "jobs", params.getPathParam(0), "checkpoints") + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + FineTuningJobCheckpointListPageAsync.of( + CheckpointServiceAsyncImpl(clientOptions), + params, + it, + ) + } } - .let { FineTuningJobCheckpointListPageAsync.of(this, params, it) } - } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/uploads/PartServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/uploads/PartServiceAsync.kt index 22c3aa2a0..15cd1a5f4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/uploads/PartServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/uploads/PartServiceAsync.kt @@ -1,5 +1,53 @@ // File generated from our OpenAPI spec by Stainless. +@file:Suppress("OVERLOADS_INTERFACE") // See https://youtrack.jetbrains.com/issue/KT-36102 + package com.openai.services.async.uploads -interface PartServiceAsync +import com.google.errorprone.annotations.MustBeClosed +import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor +import com.openai.models.UploadPart +import com.openai.models.UploadPartCreateParams +import java.util.concurrent.CompletableFuture + +interface PartServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Adds a [Part](https://platform.openai.com/docs/api-reference/uploads/part-object) to an + * [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object. A Part + * represents a chunk of bytes from the file you are trying to upload. + * + * Each Part can be at most 64 MB, and you can add Parts until you hit the Upload maximum of 8 + * GB. + * + * It is possible to add multiple Parts in parallel. You can decide the intended order of the + * Parts when you + * [complete the Upload](https://platform.openai.com/docs/api-reference/uploads/complete). + */ + @JvmOverloads + fun create( + params: UploadPartCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** A view of [PartServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /uploads/{upload_id}/parts`, but is otherwise the + * same as [PartServiceAsync.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: UploadPartCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/uploads/PartServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/uploads/PartServiceAsyncImpl.kt index b786c9377..69f82905f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/uploads/PartServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/uploads/PartServiceAsyncImpl.kt @@ -3,6 +3,71 @@ package com.openai.services.async.uploads import com.openai.core.ClientOptions +import com.openai.core.RequestOptions +import com.openai.core.handlers.errorHandler +import com.openai.core.handlers.jsonHandler +import com.openai.core.handlers.withErrorHandler +import com.openai.core.http.HttpMethod +import com.openai.core.http.HttpRequest +import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.multipartFormData +import com.openai.core.http.parseable +import com.openai.core.prepareAsync +import com.openai.errors.OpenAIError +import com.openai.models.UploadPart +import com.openai.models.UploadPartCreateParams +import java.util.concurrent.CompletableFuture class PartServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : - PartServiceAsync + PartServiceAsync { + + private val withRawResponse: PartServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): PartServiceAsync.WithRawResponse = withRawResponse + + override fun create( + params: UploadPartCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /uploads/{upload_id}/parts + withRawResponse().create(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + PartServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: UploadPartCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("uploads", params.getPathParam(0), "parts") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/AudioService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/AudioService.kt new file mode 100644 index 000000000..857ff5bc2 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/AudioService.kt @@ -0,0 +1,31 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking + +import com.openai.services.blocking.audio.SpeechService +import com.openai.services.blocking.audio.TranscriptionService +import com.openai.services.blocking.audio.TranslationService + +interface AudioService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + fun transcriptions(): TranscriptionService + + fun translations(): TranslationService + + fun speech(): SpeechService + + /** A view of [AudioService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun transcriptions(): TranscriptionService.WithRawResponse + + fun translations(): TranslationService.WithRawResponse + + fun speech(): SpeechService.WithRawResponse + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/AudioServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/AudioServiceImpl.kt new file mode 100644 index 000000000..29fac3b2f --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/AudioServiceImpl.kt @@ -0,0 +1,57 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking + +import com.openai.core.ClientOptions +import com.openai.services.blocking.audio.SpeechService +import com.openai.services.blocking.audio.SpeechServiceImpl +import com.openai.services.blocking.audio.TranscriptionService +import com.openai.services.blocking.audio.TranscriptionServiceImpl +import com.openai.services.blocking.audio.TranslationService +import com.openai.services.blocking.audio.TranslationServiceImpl + +class AudioServiceImpl internal constructor(private val clientOptions: ClientOptions) : + AudioService { + + private val withRawResponse: AudioService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val transcriptions: TranscriptionService by lazy { + TranscriptionServiceImpl(clientOptions) + } + + private val translations: TranslationService by lazy { TranslationServiceImpl(clientOptions) } + + private val speech: SpeechService by lazy { SpeechServiceImpl(clientOptions) } + + override fun withRawResponse(): AudioService.WithRawResponse = withRawResponse + + override fun transcriptions(): TranscriptionService = transcriptions + + override fun translations(): TranslationService = translations + + override fun speech(): SpeechService = speech + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + AudioService.WithRawResponse { + + private val transcriptions: TranscriptionService.WithRawResponse by lazy { + TranscriptionServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val translations: TranslationService.WithRawResponse by lazy { + TranslationServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val speech: SpeechService.WithRawResponse by lazy { + SpeechServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun transcriptions(): TranscriptionService.WithRawResponse = transcriptions + + override fun translations(): TranslationService.WithRawResponse = translations + + override fun speech(): SpeechService.WithRawResponse = speech + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/BatchService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/BatchService.kt index f7ef23057..20f6630b2 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/BatchService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/BatchService.kt @@ -4,7 +4,9 @@ package com.openai.services.blocking +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.Batch import com.openai.models.BatchCancelParams import com.openai.models.BatchCreateParams @@ -14,6 +16,11 @@ import com.openai.models.BatchRetrieveParams interface BatchService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** Creates and executes a batch from an uploaded file of requests */ @JvmOverloads fun create( @@ -49,4 +56,60 @@ interface BatchService { params: BatchCancelParams, requestOptions: RequestOptions = RequestOptions.none(), ): Batch + + /** A view of [BatchService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /batches`, but is otherwise the same as + * [BatchService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BatchCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /batches/{batch_id}`, but is otherwise the same as + * [BatchService.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BatchRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /batches`, but is otherwise the same as + * [BatchService.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: BatchListParams = BatchListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /batches`, but is otherwise the same as + * [BatchService.list]. + */ + @MustBeClosed + fun list(requestOptions: RequestOptions): HttpResponseFor = + list(BatchListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /batches/{batch_id}/cancel`, but is otherwise the + * same as [BatchService.cancel]. + */ + @JvmOverloads + @MustBeClosed + fun cancel( + params: BatchCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/BatchServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/BatchServiceImpl.kt index 3bee0335f..4fc2deb27 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/BatchServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/BatchServiceImpl.kt @@ -10,7 +10,9 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.Batch @@ -23,96 +25,139 @@ import com.openai.models.BatchRetrieveParams class BatchServiceImpl internal constructor(private val clientOptions: ClientOptions) : BatchService { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) - - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Creates and executes a batch from an uploaded file of requests */ - override fun create(params: BatchCreateParams, requestOptions: RequestOptions): Batch { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("batches") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } + private val withRawResponse: BatchService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) } - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Retrieves a batch. */ - override fun retrieve(params: BatchRetrieveParams, requestOptions: RequestOptions): Batch { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("batches", params.getPathParam(0)) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + override fun withRawResponse(): BatchService.WithRawResponse = withRawResponse + + override fun create(params: BatchCreateParams, requestOptions: RequestOptions): Batch = + // post /batches + withRawResponse().create(params, requestOptions).parse() + + override fun retrieve(params: BatchRetrieveParams, requestOptions: RequestOptions): Batch = + // get /batches/{batch_id} + withRawResponse().retrieve(params, requestOptions).parse() + + override fun list(params: BatchListParams, requestOptions: RequestOptions): BatchListPage = + // get /batches + withRawResponse().list(params, requestOptions).parse() + + override fun cancel(params: BatchCancelParams, requestOptions: RequestOptions): Batch = + // post /batches/{batch_id}/cancel + withRawResponse().cancel(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + BatchService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: BatchCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("batches") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } + } - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** List your organization's batches. */ - override fun list(params: BatchListParams, requestOptions: RequestOptions): BatchListPage { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("batches") - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BatchRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("batches", params.getPathParam(0)) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - .let { BatchListPage.of(this, params, it) } - } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: BatchListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("batches") + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { BatchListPage.of(BatchServiceImpl(clientOptions), params, it) } + } + } + + private val cancelHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - private val cancelHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Cancels an in-progress batch. The batch will be in status `cancelling` for up to 10 minutes, - * before changing to `cancelled`, where it will have partial results (if any) available in the - * output file. - */ - override fun cancel(params: BatchCancelParams, requestOptions: RequestOptions): Batch { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("batches", params.getPathParam(0), "cancel") - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { cancelHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + override fun cancel( + params: BatchCancelParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("batches", params.getPathParam(0), "cancel") + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { cancelHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/BetaService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/BetaService.kt index 2b1a724fb..0171a077c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/BetaService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/BetaService.kt @@ -8,9 +8,24 @@ import com.openai.services.blocking.beta.VectorStoreService interface BetaService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun vectorStores(): VectorStoreService fun assistants(): AssistantService fun threads(): ThreadService + + /** A view of [BetaService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun vectorStores(): VectorStoreService.WithRawResponse + + fun assistants(): AssistantService.WithRawResponse + + fun threads(): ThreadService.WithRawResponse + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/BetaServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/BetaServiceImpl.kt index cc2973baa..65a647963 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/BetaServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/BetaServiceImpl.kt @@ -12,15 +12,43 @@ import com.openai.services.blocking.beta.VectorStoreServiceImpl class BetaServiceImpl internal constructor(private val clientOptions: ClientOptions) : BetaService { + private val withRawResponse: BetaService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + private val vectorStores: VectorStoreService by lazy { VectorStoreServiceImpl(clientOptions) } private val assistants: AssistantService by lazy { AssistantServiceImpl(clientOptions) } private val threads: ThreadService by lazy { ThreadServiceImpl(clientOptions) } + override fun withRawResponse(): BetaService.WithRawResponse = withRawResponse + override fun vectorStores(): VectorStoreService = vectorStores override fun assistants(): AssistantService = assistants override fun threads(): ThreadService = threads + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + BetaService.WithRawResponse { + + private val vectorStores: VectorStoreService.WithRawResponse by lazy { + VectorStoreServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val assistants: AssistantService.WithRawResponse by lazy { + AssistantServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val threads: ThreadService.WithRawResponse by lazy { + ThreadServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun vectorStores(): VectorStoreService.WithRawResponse = vectorStores + + override fun assistants(): AssistantService.WithRawResponse = assistants + + override fun threads(): ThreadService.WithRawResponse = threads + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ChatService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ChatService.kt index c5bfb8b29..0d6aa2b0f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ChatService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ChatService.kt @@ -6,5 +6,16 @@ import com.openai.services.blocking.chat.CompletionService interface ChatService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun completions(): CompletionService + + /** A view of [ChatService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun completions(): CompletionService.WithRawResponse + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ChatServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ChatServiceImpl.kt index 2857898bc..e425493cf 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ChatServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ChatServiceImpl.kt @@ -8,7 +8,23 @@ import com.openai.services.blocking.chat.CompletionServiceImpl class ChatServiceImpl internal constructor(private val clientOptions: ClientOptions) : ChatService { + private val withRawResponse: ChatService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + private val completions: CompletionService by lazy { CompletionServiceImpl(clientOptions) } + override fun withRawResponse(): ChatService.WithRawResponse = withRawResponse + override fun completions(): CompletionService = completions + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ChatService.WithRawResponse { + + private val completions: CompletionService.WithRawResponse by lazy { + CompletionServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun completions(): CompletionService.WithRawResponse = completions + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/CompletionService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/CompletionService.kt index 3ed7d4a5c..57099e854 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/CompletionService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/CompletionService.kt @@ -6,12 +6,18 @@ package com.openai.services.blocking import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.core.http.StreamResponse import com.openai.models.Completion import com.openai.models.CompletionCreateParams interface CompletionService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** Creates a completion for the provided prompt and parameters. */ @JvmOverloads fun create( @@ -26,4 +32,30 @@ interface CompletionService { params: CompletionCreateParams, requestOptions: RequestOptions = RequestOptions.none(), ): StreamResponse + + /** A view of [CompletionService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /completions`, but is otherwise the same as + * [CompletionService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: CompletionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /completions`, but is otherwise the same as + * [CompletionService.createStreaming]. + */ + @JvmOverloads + @MustBeClosed + fun createStreaming( + params: CompletionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/CompletionServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/CompletionServiceImpl.kt index d019cbc4a..f0d8aa1fe 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/CompletionServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/CompletionServiceImpl.kt @@ -13,9 +13,11 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor import com.openai.core.http.StreamResponse +import com.openai.core.http.json import com.openai.core.http.map -import com.openai.core.json +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.Completion @@ -24,66 +26,96 @@ import com.openai.models.CompletionCreateParams class CompletionServiceImpl internal constructor(private val clientOptions: ClientOptions) : CompletionService { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: CompletionService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun withRawResponse(): CompletionService.WithRawResponse = withRawResponse - /** Creates a completion for the provided prompt and parameters. */ override fun create( params: CompletionCreateParams, requestOptions: RequestOptions, - ): Completion { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("completions") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, params.model().toString()) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): Completion = + // post /completions + withRawResponse().create(params, requestOptions).parse() - private val createStreamingHandler: Handler> = - sseHandler(clientOptions.jsonMapper).mapJson().withErrorHandler(errorHandler) - - /** Creates a completion for the provided prompt and parameters. */ override fun createStreaming( params: CompletionCreateParams, requestOptions: RequestOptions, - ): StreamResponse { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("completions") - .body( - json( - clientOptions.jsonMapper, - params - ._body() - .toBuilder() - .putAdditionalProperty("stream", JsonValue.from(true)) - .build(), + ): StreamResponse = + // post /completions + withRawResponse().createStreaming(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CompletionService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: CompletionCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("completions") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val createStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper) + .mapJson() + .withErrorHandler(errorHandler) + + override fun createStreaming( + params: CompletionCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("completions") + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("stream", JsonValue.from(true)) + .build(), + ) ) - ) - .build() - .prepare(clientOptions, params, params.model().toString()) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .let { createStreamingHandler.handle(it) } - .let { streamResponse -> - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - streamResponse.map { it.validate() } - } else { - streamResponse - } + .build() + .prepare(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .let { createStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/EmbeddingService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/EmbeddingService.kt index 3941f8c01..de2aba538 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/EmbeddingService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/EmbeddingService.kt @@ -4,16 +4,38 @@ package com.openai.services.blocking +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.CreateEmbeddingResponse import com.openai.models.EmbeddingCreateParams interface EmbeddingService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** Creates an embedding vector representing the input text. */ @JvmOverloads fun create( params: EmbeddingCreateParams, requestOptions: RequestOptions = RequestOptions.none(), ): CreateEmbeddingResponse + + /** A view of [EmbeddingService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /embeddings`, but is otherwise the same as + * [EmbeddingService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: EmbeddingCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/EmbeddingServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/EmbeddingServiceImpl.kt index 29171a7cb..73a227fd5 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/EmbeddingServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/EmbeddingServiceImpl.kt @@ -10,7 +10,9 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.CreateEmbeddingResponse @@ -19,31 +21,50 @@ import com.openai.models.EmbeddingCreateParams class EmbeddingServiceImpl internal constructor(private val clientOptions: ClientOptions) : EmbeddingService { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: EmbeddingService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + override fun withRawResponse(): EmbeddingService.WithRawResponse = withRawResponse - /** Creates an embedding vector representing the input text. */ override fun create( params: EmbeddingCreateParams, requestOptions: RequestOptions, - ): CreateEmbeddingResponse { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("embeddings") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, params.model().toString()) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): CreateEmbeddingResponse = + // post /embeddings + withRawResponse().create(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + EmbeddingService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun create( + params: EmbeddingCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("embeddings") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/FileService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/FileService.kt index edcc81caf..e82ed32a5 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/FileService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/FileService.kt @@ -7,7 +7,9 @@ package com.openai.services.blocking import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions import com.openai.core.http.HttpResponse +import com.openai.core.http.HttpResponseFor import com.openai.models.FileContentParams +import com.openai.models.FileCreateParams import com.openai.models.FileDeleteParams import com.openai.models.FileDeleted import com.openai.models.FileListPage @@ -17,6 +19,36 @@ import com.openai.models.FileRetrieveParams interface FileService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Upload a file that can be used across various endpoints. Individual files can be up to 512 + * MB, and the size of all files uploaded by one organization can be up to 100 GB. + * + * The Assistants API supports files up to 2 million tokens and of specific file types. See the + * [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) for details. + * + * The Fine-tuning API only supports `.jsonl` files. The input also has certain required formats + * for fine-tuning [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) + * or + * [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) + * models. + * + * The Batch API only supports `.jsonl` files up to 200 MB in size. The input also has a + * specific required + * [format](https://platform.openai.com/docs/api-reference/batch/request-input). + * + * Please [contact us](https://help.openai.com/) if you need to increase these storage limits. + */ + @JvmOverloads + fun create( + params: FileCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): FileObject + /** Returns information about a specific file. */ @JvmOverloads fun retrieve( @@ -49,4 +81,71 @@ interface FileService { params: FileContentParams, requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponse + + /** A view of [FileService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /files`, but is otherwise the same as + * [FileService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: FileCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /files/{file_id}`, but is otherwise the same as + * [FileService.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: FileRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /files`, but is otherwise the same as + * [FileService.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: FileListParams = FileListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /files`, but is otherwise the same as + * [FileService.list]. + */ + @MustBeClosed + fun list(requestOptions: RequestOptions): HttpResponseFor = + list(FileListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /files/{file_id}`, but is otherwise the same as + * [FileService.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: FileDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /files/{file_id}/content`, but is otherwise the same + * as [FileService.content]. + */ + @JvmOverloads + @MustBeClosed + fun content( + params: FileContentParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/FileServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/FileServiceImpl.kt index c0983adf5..89aca2fad 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/FileServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/FileServiceImpl.kt @@ -11,10 +11,14 @@ import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.multipartFormData +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.FileContentParams +import com.openai.models.FileCreateParams import com.openai.models.FileDeleteParams import com.openai.models.FileDeleted import com.openai.models.FileListPage @@ -24,81 +28,157 @@ import com.openai.models.FileRetrieveParams class FileServiceImpl internal constructor(private val clientOptions: ClientOptions) : FileService { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) - - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Returns information about a specific file. */ - override fun retrieve(params: FileRetrieveParams, requestOptions: RequestOptions): FileObject { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("files", params.getPathParam(0)) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } + private val withRawResponse: FileService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) } - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Returns a list of files. */ - override fun list(params: FileListParams, requestOptions: RequestOptions): FileListPage { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("files") - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + override fun withRawResponse(): FileService.WithRawResponse = withRawResponse + + override fun create(params: FileCreateParams, requestOptions: RequestOptions): FileObject = + // post /files + withRawResponse().create(params, requestOptions).parse() + + override fun retrieve(params: FileRetrieveParams, requestOptions: RequestOptions): FileObject = + // get /files/{file_id} + withRawResponse().retrieve(params, requestOptions).parse() + + override fun list(params: FileListParams, requestOptions: RequestOptions): FileListPage = + // get /files + withRawResponse().list(params, requestOptions).parse() + + override fun delete(params: FileDeleteParams, requestOptions: RequestOptions): FileDeleted = + // delete /files/{file_id} + withRawResponse().delete(params, requestOptions).parse() + + override fun content(params: FileContentParams, requestOptions: RequestOptions): HttpResponse = + // get /files/{file_id}/content + withRawResponse().content(params, requestOptions) + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + FileService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: FileCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("files") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - .let { FileListPage.of(this, params, it) } - } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Delete a file. */ - override fun delete(params: FileDeleteParams, requestOptions: RequestOptions): FileDeleted { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments("files", params.getPathParam(0)) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + override fun retrieve( + params: FileRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("files", params.getPathParam(0)) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: FileListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("files") + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { FileListPage.of(FileServiceImpl(clientOptions), params, it) } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun delete( + params: FileDeleteParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments("files", params.getPathParam(0)) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } - /** Returns the contents of the specified file. */ - override fun content(params: FileContentParams, requestOptions: RequestOptions): HttpResponse { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("files", params.getPathParam(0), "content") - .build() - .prepare(clientOptions, params, deploymentModel = null) - return clientOptions.httpClient.execute(request, requestOptions) + override fun content( + params: FileContentParams, + requestOptions: RequestOptions, + ): HttpResponse { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("files", params.getPathParam(0), "content") + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return clientOptions.httpClient.execute(request, requestOptions) + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/FineTuningService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/FineTuningService.kt index 22f54053f..0bf5dec64 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/FineTuningService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/FineTuningService.kt @@ -6,5 +6,16 @@ import com.openai.services.blocking.fineTuning.JobService interface FineTuningService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun jobs(): JobService + + /** A view of [FineTuningService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun jobs(): JobService.WithRawResponse + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/FineTuningServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/FineTuningServiceImpl.kt index 34433c955..4a7b87bd6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/FineTuningServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/FineTuningServiceImpl.kt @@ -9,7 +9,23 @@ import com.openai.services.blocking.fineTuning.JobServiceImpl class FineTuningServiceImpl internal constructor(private val clientOptions: ClientOptions) : FineTuningService { + private val withRawResponse: FineTuningService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + private val jobs: JobService by lazy { JobServiceImpl(clientOptions) } + override fun withRawResponse(): FineTuningService.WithRawResponse = withRawResponse + override fun jobs(): JobService = jobs + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + FineTuningService.WithRawResponse { + + private val jobs: JobService.WithRawResponse by lazy { + JobServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun jobs(): JobService.WithRawResponse = jobs + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ImageService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ImageService.kt index e066013e6..7a5052b57 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ImageService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ImageService.kt @@ -4,16 +4,76 @@ package com.openai.services.blocking +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor +import com.openai.models.ImageCreateVariationParams +import com.openai.models.ImageEditParams import com.openai.models.ImageGenerateParams import com.openai.models.ImagesResponse interface ImageService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** Creates a variation of a given image. */ + @JvmOverloads + fun createVariation( + params: ImageCreateVariationParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ImagesResponse + + /** Creates an edited or extended image given an original image and a prompt. */ + @JvmOverloads + fun edit( + params: ImageEditParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ImagesResponse + /** Creates an image given a prompt. */ @JvmOverloads fun generate( params: ImageGenerateParams, requestOptions: RequestOptions = RequestOptions.none(), ): ImagesResponse + + /** A view of [ImageService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /images/variations`, but is otherwise the same as + * [ImageService.createVariation]. + */ + @JvmOverloads + @MustBeClosed + fun createVariation( + params: ImageCreateVariationParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /images/edits`, but is otherwise the same as + * [ImageService.edit]. + */ + @JvmOverloads + @MustBeClosed + fun edit( + params: ImageEditParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /images/generations`, but is otherwise the same as + * [ImageService.generate]. + */ + @JvmOverloads + @MustBeClosed + fun generate( + params: ImageGenerateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ImageServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ImageServiceImpl.kt index f9c3dcaa7..efeb0d54c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ImageServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ImageServiceImpl.kt @@ -10,39 +10,136 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.multipartFormData +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError +import com.openai.models.ImageCreateVariationParams +import com.openai.models.ImageEditParams import com.openai.models.ImageGenerateParams import com.openai.models.ImagesResponse class ImageServiceImpl internal constructor(private val clientOptions: ClientOptions) : ImageService { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: ImageService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): ImageService.WithRawResponse = withRawResponse + + override fun createVariation( + params: ImageCreateVariationParams, + requestOptions: RequestOptions, + ): ImagesResponse = + // post /images/variations + withRawResponse().createVariation(params, requestOptions).parse() - private val generateHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun edit(params: ImageEditParams, requestOptions: RequestOptions): ImagesResponse = + // post /images/edits + withRawResponse().edit(params, requestOptions).parse() - /** Creates an image given a prompt. */ override fun generate( params: ImageGenerateParams, requestOptions: RequestOptions, - ): ImagesResponse { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("images", "generations") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, params.model().toString()) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { generateHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): ImagesResponse = + // post /images/generations + withRawResponse().generate(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ImageService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createVariationHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun createVariation( + params: ImageCreateVariationParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("images", "variations") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepare( + clientOptions, + params, + deploymentModel = params.model().map { it.toString() }.orElse(null), + ) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createVariationHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val editHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun edit( + params: ImageEditParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("images", "edits") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepare( + clientOptions, + params, + deploymentModel = params.model().map { it.toString() }.orElse(null), + ) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { editHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val generateHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun generate( + params: ImageGenerateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("images", "generations") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { generateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModelService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModelService.kt index 412ff7445..dd0538bf6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModelService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModelService.kt @@ -4,7 +4,9 @@ package com.openai.services.blocking +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.Model import com.openai.models.ModelDeleteParams import com.openai.models.ModelDeleted @@ -14,6 +16,11 @@ import com.openai.models.ModelRetrieveParams interface ModelService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** * Retrieves a model instance, providing basic information about the model such as the owner and * permissioning. @@ -50,4 +57,49 @@ interface ModelService { params: ModelDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): ModelDeleted + + /** A view of [ModelService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `get /models/{model}`, but is otherwise the same as + * [ModelService.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: ModelRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /models`, but is otherwise the same as + * [ModelService.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: ModelListParams = ModelListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /models`, but is otherwise the same as + * [ModelService.list]. + */ + @MustBeClosed + fun list(requestOptions: RequestOptions): HttpResponseFor = + list(ModelListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /models/{model}`, but is otherwise the same as + * [ModelService.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: ModelDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModelServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModelServiceImpl.kt index 4f984a54d..f2af5f122 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModelServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModelServiceImpl.kt @@ -10,7 +10,9 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.Model @@ -23,79 +25,108 @@ import com.openai.models.ModelRetrieveParams class ModelServiceImpl internal constructor(private val clientOptions: ClientOptions) : ModelService { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) - - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Retrieves a model instance, providing basic information about the model such as the owner and - * permissioning. - */ - override fun retrieve(params: ModelRetrieveParams, requestOptions: RequestOptions): Model { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("models", params.getPathParam(0)) - .build() - .prepare(clientOptions, params, params.model()) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } + private val withRawResponse: ModelService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) } - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Lists the currently available models, and provides basic information about each one such as - * the owner and availability. - */ - override fun list(params: ModelListParams, requestOptions: RequestOptions): ModelListPage { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("models") - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + override fun withRawResponse(): ModelService.WithRawResponse = withRawResponse + + override fun retrieve(params: ModelRetrieveParams, requestOptions: RequestOptions): Model = + // get /models/{model} + withRawResponse().retrieve(params, requestOptions).parse() + + override fun list(params: ModelListParams, requestOptions: RequestOptions): ModelListPage = + // get /models + withRawResponse().list(params, requestOptions).parse() + + override fun delete(params: ModelDeleteParams, requestOptions: RequestOptions): ModelDeleted = + // delete /models/{model} + withRawResponse().delete(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ModelService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: ModelRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("models", params.getPathParam(0)) + .build() + .prepare(clientOptions, params, params.model()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - .let { ModelListPage.of(this, params, it) } - } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: ModelListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("models") + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { ModelListPage.of(ModelServiceImpl(clientOptions), params, it) } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Delete a fine-tuned model. You must have the Owner role in your organization to delete a - * model. - */ - override fun delete(params: ModelDeleteParams, requestOptions: RequestOptions): ModelDeleted { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments("models", params.getPathParam(0)) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepare(clientOptions, params, params.model()) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + override fun delete( + params: ModelDeleteParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments("models", params.getPathParam(0)) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params, params.model()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModerationService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModerationService.kt index b46755f0b..278caaa8e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModerationService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModerationService.kt @@ -4,12 +4,19 @@ package com.openai.services.blocking +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.ModerationCreateParams import com.openai.models.ModerationCreateResponse interface ModerationService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** * Classifies if text and/or image inputs are potentially harmful. Learn more in the * [moderation guide](https://platform.openai.com/docs/guides/moderation). @@ -19,4 +26,19 @@ interface ModerationService { params: ModerationCreateParams, requestOptions: RequestOptions = RequestOptions.none(), ): ModerationCreateResponse + + /** A view of [ModerationService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /moderations`, but is otherwise the same as + * [ModerationService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: ModerationCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModerationServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModerationServiceImpl.kt index 34b73160c..65242f507 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModerationServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModerationServiceImpl.kt @@ -10,7 +10,9 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.ModerationCreateParams @@ -19,34 +21,50 @@ import com.openai.models.ModerationCreateResponse class ModerationServiceImpl internal constructor(private val clientOptions: ClientOptions) : ModerationService { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: ModerationService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + override fun withRawResponse(): ModerationService.WithRawResponse = withRawResponse - /** - * Classifies if text and/or image inputs are potentially harmful. Learn more in the - * [moderation guide](https://platform.openai.com/docs/guides/moderation). - */ override fun create( params: ModerationCreateParams, requestOptions: RequestOptions, - ): ModerationCreateResponse { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("moderations") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, params.model().toString()) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): ModerationCreateResponse = + // post /moderations + withRawResponse().create(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ModerationService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun create( + params: ModerationCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("moderations") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/UploadService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/UploadService.kt index 96061582f..f30efc531 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/UploadService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/UploadService.kt @@ -4,7 +4,9 @@ package com.openai.services.blocking +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.Upload import com.openai.models.UploadCancelParams import com.openai.models.UploadCompleteParams @@ -13,6 +15,11 @@ import com.openai.services.blocking.uploads.PartService interface UploadService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun parts(): PartService /** @@ -66,4 +73,43 @@ interface UploadService { params: UploadCompleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): Upload + + /** A view of [UploadService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun parts(): PartService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /uploads`, but is otherwise the same as + * [UploadService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: UploadCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /uploads/{upload_id}/cancel`, but is otherwise the + * same as [UploadService.cancel]. + */ + @JvmOverloads + @MustBeClosed + fun cancel( + params: UploadCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /uploads/{upload_id}/complete`, but is otherwise + * the same as [UploadService.complete]. + */ + @JvmOverloads + @MustBeClosed + fun complete( + params: UploadCompleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/UploadServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/UploadServiceImpl.kt index 3a327caee..1563e963a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/UploadServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/UploadServiceImpl.kt @@ -10,7 +10,9 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.Upload @@ -23,106 +25,118 @@ import com.openai.services.blocking.uploads.PartServiceImpl class UploadServiceImpl internal constructor(private val clientOptions: ClientOptions) : UploadService { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: UploadService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } private val parts: PartService by lazy { PartServiceImpl(clientOptions) } + override fun withRawResponse(): UploadService.WithRawResponse = withRawResponse + override fun parts(): PartService = parts - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Creates an intermediate - * [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object that you can - * add [Parts](https://platform.openai.com/docs/api-reference/uploads/part-object) to. - * Currently, an Upload can accept at most 8 GB in total and expires after an hour after you - * create it. - * - * Once you complete the Upload, we will create a - * [File](https://platform.openai.com/docs/api-reference/files/object) object that contains all - * the parts you uploaded. This File is usable in the rest of our platform as a regular File - * object. - * - * For certain `purpose`s, the correct `mime_type` must be specified. Please refer to - * documentation for the supported MIME types for your use case: - * - [Assistants](https://platform.openai.com/docs/assistants/tools/file-search#supported-files) - * - * For guidance on the proper filename extensions for each purpose, please follow the - * documentation on - * [creating a File](https://platform.openai.com/docs/api-reference/files/create). - */ - override fun create(params: UploadCreateParams, requestOptions: RequestOptions): Upload { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("uploads") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + override fun create(params: UploadCreateParams, requestOptions: RequestOptions): Upload = + // post /uploads + withRawResponse().create(params, requestOptions).parse() + + override fun cancel(params: UploadCancelParams, requestOptions: RequestOptions): Upload = + // post /uploads/{upload_id}/cancel + withRawResponse().cancel(params, requestOptions).parse() + + override fun complete(params: UploadCompleteParams, requestOptions: RequestOptions): Upload = + // post /uploads/{upload_id}/complete + withRawResponse().complete(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + UploadService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val parts: PartService.WithRawResponse by lazy { + PartServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun parts(): PartService.WithRawResponse = parts + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: UploadCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("uploads") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } + } - private val cancelHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Cancels the Upload. No Parts may be added after an Upload is cancelled. */ - override fun cancel(params: UploadCancelParams, requestOptions: RequestOptions): Upload { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("uploads", params.getPathParam(0), "cancel") - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { cancelHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + private val cancelHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun cancel( + params: UploadCancelParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("uploads", params.getPathParam(0), "cancel") + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { cancelHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } - } + } + + private val completeHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - private val completeHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Completes the [Upload](https://platform.openai.com/docs/api-reference/uploads/object). - * - * Within the returned Upload object, there is a nested - * [File](https://platform.openai.com/docs/api-reference/files/object) object that is ready to - * use in the rest of the platform. - * - * You can specify the order of the Parts by passing in an ordered list of the Part IDs. - * - * The number of bytes uploaded upon completion must match the number of bytes initially - * specified when creating the Upload object. No Parts may be added after an Upload is - * completed. - */ - override fun complete(params: UploadCompleteParams, requestOptions: RequestOptions): Upload { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("uploads", params.getPathParam(0), "complete") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { completeHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + override fun complete( + params: UploadCompleteParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("uploads", params.getPathParam(0), "complete") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { completeHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/SpeechService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/SpeechService.kt new file mode 100644 index 000000000..42268f776 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/SpeechService.kt @@ -0,0 +1,41 @@ +// File generated from our OpenAPI spec by Stainless. + +@file:Suppress("OVERLOADS_INTERFACE") // See https://youtrack.jetbrains.com/issue/KT-36102 + +package com.openai.services.blocking.audio + +import com.google.errorprone.annotations.MustBeClosed +import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponse +import com.openai.models.AudioSpeechCreateParams + +interface SpeechService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** Generates audio from the input text. */ + @JvmOverloads + @MustBeClosed + fun create( + params: AudioSpeechCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse + + /** A view of [SpeechService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /audio/speech`, but is otherwise the same as + * [SpeechService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: AudioSpeechCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/SpeechServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/SpeechServiceImpl.kt new file mode 100644 index 000000000..f9b8913b5 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/SpeechServiceImpl.kt @@ -0,0 +1,53 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.audio + +import com.openai.core.ClientOptions +import com.openai.core.RequestOptions +import com.openai.core.handlers.errorHandler +import com.openai.core.http.HttpMethod +import com.openai.core.http.HttpRequest +import com.openai.core.http.HttpResponse +import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.json +import com.openai.core.prepare +import com.openai.errors.OpenAIError +import com.openai.models.AudioSpeechCreateParams + +class SpeechServiceImpl internal constructor(private val clientOptions: ClientOptions) : + SpeechService { + + private val withRawResponse: SpeechService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): SpeechService.WithRawResponse = withRawResponse + + override fun create( + params: AudioSpeechCreateParams, + requestOptions: RequestOptions, + ): HttpResponse = + // post /audio/speech + withRawResponse().create(params, requestOptions) + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + SpeechService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + override fun create( + params: AudioSpeechCreateParams, + requestOptions: RequestOptions, + ): HttpResponse { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("audio", "speech") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return clientOptions.httpClient.execute(request, requestOptions) + } + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/TranscriptionService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/TranscriptionService.kt new file mode 100644 index 000000000..9648b7674 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/TranscriptionService.kt @@ -0,0 +1,43 @@ +// File generated from our OpenAPI spec by Stainless. + +@file:Suppress("OVERLOADS_INTERFACE") // See https://youtrack.jetbrains.com/issue/KT-36102 + +package com.openai.services.blocking.audio + +import com.google.errorprone.annotations.MustBeClosed +import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor +import com.openai.models.AudioTranscriptionCreateParams +import com.openai.models.AudioTranscriptionCreateResponse + +interface TranscriptionService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** Transcribes audio into the input language. */ + @JvmOverloads + fun create( + params: AudioTranscriptionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): AudioTranscriptionCreateResponse + + /** + * A view of [TranscriptionService] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /audio/transcriptions`, but is otherwise the same + * as [TranscriptionService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: AudioTranscriptionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/TranscriptionServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/TranscriptionServiceImpl.kt new file mode 100644 index 000000000..8d28b9dc1 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/TranscriptionServiceImpl.kt @@ -0,0 +1,70 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.audio + +import com.openai.core.ClientOptions +import com.openai.core.RequestOptions +import com.openai.core.handlers.errorHandler +import com.openai.core.handlers.jsonHandler +import com.openai.core.handlers.withErrorHandler +import com.openai.core.http.HttpMethod +import com.openai.core.http.HttpRequest +import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.multipartFormData +import com.openai.core.http.parseable +import com.openai.core.prepare +import com.openai.errors.OpenAIError +import com.openai.models.AudioTranscriptionCreateParams +import com.openai.models.AudioTranscriptionCreateResponse + +class TranscriptionServiceImpl internal constructor(private val clientOptions: ClientOptions) : + TranscriptionService { + + private val withRawResponse: TranscriptionService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): TranscriptionService.WithRawResponse = withRawResponse + + override fun create( + params: AudioTranscriptionCreateParams, + requestOptions: RequestOptions, + ): AudioTranscriptionCreateResponse = + // post /audio/transcriptions + withRawResponse().create(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + TranscriptionService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun create( + params: AudioTranscriptionCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("audio", "transcriptions") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/TranslationService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/TranslationService.kt new file mode 100644 index 000000000..5e16b8527 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/TranslationService.kt @@ -0,0 +1,43 @@ +// File generated from our OpenAPI spec by Stainless. + +@file:Suppress("OVERLOADS_INTERFACE") // See https://youtrack.jetbrains.com/issue/KT-36102 + +package com.openai.services.blocking.audio + +import com.google.errorprone.annotations.MustBeClosed +import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor +import com.openai.models.AudioTranslationCreateParams +import com.openai.models.AudioTranslationCreateResponse + +interface TranslationService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** Translates audio into English. */ + @JvmOverloads + fun create( + params: AudioTranslationCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): AudioTranslationCreateResponse + + /** + * A view of [TranslationService] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /audio/translations`, but is otherwise the same as + * [TranslationService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: AudioTranslationCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/TranslationServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/TranslationServiceImpl.kt new file mode 100644 index 000000000..9ea6a0136 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/audio/TranslationServiceImpl.kt @@ -0,0 +1,70 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.audio + +import com.openai.core.ClientOptions +import com.openai.core.RequestOptions +import com.openai.core.handlers.errorHandler +import com.openai.core.handlers.jsonHandler +import com.openai.core.handlers.withErrorHandler +import com.openai.core.http.HttpMethod +import com.openai.core.http.HttpRequest +import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.multipartFormData +import com.openai.core.http.parseable +import com.openai.core.prepare +import com.openai.errors.OpenAIError +import com.openai.models.AudioTranslationCreateParams +import com.openai.models.AudioTranslationCreateResponse + +class TranslationServiceImpl internal constructor(private val clientOptions: ClientOptions) : + TranslationService { + + private val withRawResponse: TranslationService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): TranslationService.WithRawResponse = withRawResponse + + override fun create( + params: AudioTranslationCreateParams, + requestOptions: RequestOptions, + ): AudioTranslationCreateResponse = + // post /audio/translations + withRawResponse().create(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + TranslationService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun create( + params: AudioTranslationCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("audio", "translations") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/AssistantService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/AssistantService.kt index e81e43458..ed680a33e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/AssistantService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/AssistantService.kt @@ -4,7 +4,9 @@ package com.openai.services.blocking.beta +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.Assistant import com.openai.models.AssistantDeleted import com.openai.models.BetaAssistantCreateParams @@ -16,6 +18,11 @@ import com.openai.models.BetaAssistantUpdateParams interface AssistantService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** Create an assistant with a model and instructions. */ @JvmOverloads fun create( @@ -54,4 +61,71 @@ interface AssistantService { params: BetaAssistantDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): AssistantDeleted + + /** A view of [AssistantService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /assistants`, but is otherwise the same as + * [AssistantService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BetaAssistantCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /assistants/{assistant_id}`, but is otherwise the + * same as [AssistantService.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaAssistantRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /assistants/{assistant_id}`, but is otherwise the + * same as [AssistantService.update]. + */ + @JvmOverloads + @MustBeClosed + fun update( + params: BetaAssistantUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /assistants`, but is otherwise the same as + * [AssistantService.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: BetaAssistantListParams = BetaAssistantListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /assistants`, but is otherwise the same as + * [AssistantService.list]. + */ + @MustBeClosed + fun list(requestOptions: RequestOptions): HttpResponseFor = + list(BetaAssistantListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /assistants/{assistant_id}`, but is otherwise the + * same as [AssistantService.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: BetaAssistantDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/AssistantServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/AssistantServiceImpl.kt index 538cb3704..5f80fb40e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/AssistantServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/AssistantServiceImpl.kt @@ -11,7 +11,9 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.Assistant @@ -31,135 +33,196 @@ class AssistantServiceImpl internal constructor(private val clientOptions: Clien private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: AssistantService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun withRawResponse(): AssistantService.WithRawResponse = withRawResponse - /** Create an assistant with a model and instructions. */ override fun create( params: BetaAssistantCreateParams, requestOptions: RequestOptions, - ): Assistant { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("assistants") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, params.model().toString()) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): Assistant = + // post /assistants + withRawResponse().create(params, requestOptions).parse() - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Retrieves an assistant. */ override fun retrieve( params: BetaAssistantRetrieveParams, requestOptions: RequestOptions, - ): Assistant { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("assistants", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - - private val updateHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): Assistant = + // get /assistants/{assistant_id} + withRawResponse().retrieve(params, requestOptions).parse() - /** Modifies an assistant. */ override fun update( params: BetaAssistantUpdateParams, requestOptions: RequestOptions, - ): Assistant { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("assistants", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, params.model().map { it.toString() }.orElse(null)) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { updateHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): Assistant = + // post /assistants/{assistant_id} + withRawResponse().update(params, requestOptions).parse() - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) - - /** Returns a list of assistants. */ override fun list( params: BetaAssistantListParams, requestOptions: RequestOptions, - ): BetaAssistantListPage { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("assistants") - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - .let { BetaAssistantListPage.of(this, params, it) } - } - - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): BetaAssistantListPage = + // get /assistants + withRawResponse().list(params, requestOptions).parse() - /** Delete an assistant. */ override fun delete( params: BetaAssistantDeleteParams, requestOptions: RequestOptions, - ): AssistantDeleted { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments("assistants", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): AssistantDeleted = + // delete /assistants/{assistant_id} + withRawResponse().delete(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + AssistantService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: BetaAssistantCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("assistants") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaAssistantRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("assistants", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun update( + params: BetaAssistantUpdateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("assistants", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare( + clientOptions, + params, + params.model().map { it.toString() }.orElse(null), + ) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: BetaAssistantListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("assistants") + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + BetaAssistantListPage.of(AssistantServiceImpl(clientOptions), params, it) + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun delete( + params: BetaAssistantDeleteParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments("assistants", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/ThreadService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/ThreadService.kt index 2c240b53d..6036e0eb4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/ThreadService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/ThreadService.kt @@ -6,6 +6,7 @@ package com.openai.services.blocking.beta import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.core.http.StreamResponse import com.openai.models.AssistantStreamEvent import com.openai.models.BetaThreadCreateAndRunParams @@ -21,6 +22,11 @@ import com.openai.services.blocking.beta.threads.RunService interface ThreadService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun runs(): RunService fun messages(): MessageService @@ -71,4 +77,86 @@ interface ThreadService { params: BetaThreadCreateAndRunParams, requestOptions: RequestOptions = RequestOptions.none(), ): StreamResponse + + /** A view of [ThreadService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun runs(): RunService.WithRawResponse + + fun messages(): MessageService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /threads`, but is otherwise the same as + * [ThreadService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BetaThreadCreateParams = BetaThreadCreateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /threads`, but is otherwise the same as + * [ThreadService.create]. + */ + @MustBeClosed + fun create(requestOptions: RequestOptions): HttpResponseFor = + create(BetaThreadCreateParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /threads/{thread_id}`, but is otherwise the same as + * [ThreadService.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaThreadRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /threads/{thread_id}`, but is otherwise the same as + * [ThreadService.update]. + */ + @JvmOverloads + @MustBeClosed + fun update( + params: BetaThreadUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `delete /threads/{thread_id}`, but is otherwise the same + * as [ThreadService.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: BetaThreadDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /threads/runs`, but is otherwise the same as + * [ThreadService.createAndRun]. + */ + @JvmOverloads + @MustBeClosed + fun createAndRun( + params: BetaThreadCreateAndRunParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /threads/runs`, but is otherwise the same as + * [ThreadService.createAndRunStreaming]. + */ + @JvmOverloads + @MustBeClosed + fun createAndRunStreaming( + params: BetaThreadCreateAndRunParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/ThreadServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/ThreadServiceImpl.kt index 632615a7f..18d11c991 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/ThreadServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/ThreadServiceImpl.kt @@ -14,9 +14,11 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor import com.openai.core.http.StreamResponse +import com.openai.core.http.json import com.openai.core.http.map -import com.openai.core.json +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.AssistantStreamEvent @@ -41,175 +43,259 @@ class ThreadServiceImpl internal constructor(private val clientOptions: ClientOp private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: ThreadService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } private val runs: RunService by lazy { RunServiceImpl(clientOptions) } private val messages: MessageService by lazy { MessageServiceImpl(clientOptions) } + override fun withRawResponse(): ThreadService.WithRawResponse = withRawResponse + override fun runs(): RunService = runs override fun messages(): MessageService = messages - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Create a thread. */ - override fun create(params: BetaThreadCreateParams, requestOptions: RequestOptions): Thread { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun create(params: BetaThreadCreateParams, requestOptions: RequestOptions): Thread = + // post /threads + withRawResponse().create(params, requestOptions).parse() - /** Retrieves a thread. */ override fun retrieve( params: BetaThreadRetrieveParams, requestOptions: RequestOptions, - ): Thread { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("threads", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): Thread = + // get /threads/{thread_id} + withRawResponse().retrieve(params, requestOptions).parse() - private val updateHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Modifies a thread. */ - override fun update(params: BetaThreadUpdateParams, requestOptions: RequestOptions): Thread { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { updateHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + override fun update(params: BetaThreadUpdateParams, requestOptions: RequestOptions): Thread = + // post /threads/{thread_id} + withRawResponse().update(params, requestOptions).parse() - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Delete a thread. */ override fun delete( params: BetaThreadDeleteParams, requestOptions: RequestOptions, - ): ThreadDeleted { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments("threads", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - - private val createAndRunHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): ThreadDeleted = + // delete /threads/{thread_id} + withRawResponse().delete(params, requestOptions).parse() - /** Create a thread and run it in one request. */ override fun createAndRun( params: BetaThreadCreateAndRunParams, requestOptions: RequestOptions, - ): Run { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads", "runs") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, params.model().map { it.toString() }.orElse(null)) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { createAndRunHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - - private val createAndRunStreamingHandler: Handler> = - sseHandler(clientOptions.jsonMapper) - .mapJson(includeEventAndData = true) - .withErrorHandler(errorHandler) + ): Run = + // post /threads/runs + withRawResponse().createAndRun(params, requestOptions).parse() - /** Create a thread and run it in one request. */ override fun createAndRunStreaming( params: BetaThreadCreateAndRunParams, requestOptions: RequestOptions, - ): StreamResponse { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads", "runs") - .putAllHeaders(DEFAULT_HEADERS) - .body( - json( - clientOptions.jsonMapper, - params - ._body() - .toBuilder() - .putAdditionalProperty("stream", JsonValue.from(true)) - .build(), + ): StreamResponse = + // post /threads/runs + withRawResponse().createAndRunStreaming(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ThreadService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val runs: RunService.WithRawResponse by lazy { + RunServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val messages: MessageService.WithRawResponse by lazy { + MessageServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun runs(): RunService.WithRawResponse = runs + + override fun messages(): MessageService.WithRawResponse = messages + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: BetaThreadCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("threads") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaThreadRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("threads", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun update( + params: BetaThreadUpdateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("threads", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun delete( + params: BetaThreadDeleteParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments("threads", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val createAndRunHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun createAndRun( + params: BetaThreadCreateAndRunParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("threads", "runs") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare( + clientOptions, + params, + params.model().map { it.toString() }.orElse(null), + ) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createAndRunHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val createAndRunStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper) + .mapJson(includeEventAndData = true) + .withErrorHandler(errorHandler) + + override fun createAndRunStreaming( + params: BetaThreadCreateAndRunParams, + requestOptions: RequestOptions, + ): HttpResponseFor> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("threads", "runs") + .putAllHeaders(DEFAULT_HEADERS) + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("stream", JsonValue.from(true)) + .build(), + ) + ) + .build() + .prepare( + clientOptions, + params, + params.model().map { it.toString() }.orElse(null), ) - ) - .build() - .prepare(clientOptions, params, params.model().map { it.toString() }.orElse(null)) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .let { createAndRunStreamingHandler.handle(it) } - .let { streamResponse -> - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - streamResponse.map { it.validate() } - } else { - streamResponse - } + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .let { createAndRunStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/VectorStoreService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/VectorStoreService.kt index ae9303dc3..45feb920c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/VectorStoreService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/VectorStoreService.kt @@ -4,7 +4,9 @@ package com.openai.services.blocking.beta +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.BetaVectorStoreCreateParams import com.openai.models.BetaVectorStoreDeleteParams import com.openai.models.BetaVectorStoreListPage @@ -18,6 +20,11 @@ import com.openai.services.blocking.beta.vectorStores.FileService interface VectorStoreService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun files(): FileService fun fileBatches(): FileBatchService @@ -60,4 +67,77 @@ interface VectorStoreService { params: BetaVectorStoreDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): VectorStoreDeleted + + /** + * A view of [VectorStoreService] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + fun files(): FileService.WithRawResponse + + fun fileBatches(): FileBatchService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /vector_stores`, but is otherwise the same as + * [VectorStoreService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BetaVectorStoreCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /vector_stores/{vector_store_id}`, but is otherwise + * the same as [VectorStoreService.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaVectorStoreRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /vector_stores/{vector_store_id}`, but is otherwise + * the same as [VectorStoreService.update]. + */ + @JvmOverloads + @MustBeClosed + fun update( + params: BetaVectorStoreUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /vector_stores`, but is otherwise the same as + * [VectorStoreService.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: BetaVectorStoreListParams = BetaVectorStoreListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /vector_stores`, but is otherwise the same as + * [VectorStoreService.list]. + */ + @MustBeClosed + fun list(requestOptions: RequestOptions): HttpResponseFor = + list(BetaVectorStoreListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /vector_stores/{vector_store_id}`, but is + * otherwise the same as [VectorStoreService.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: BetaVectorStoreDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/VectorStoreServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/VectorStoreServiceImpl.kt index 8ec171db7..114f93642 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/VectorStoreServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/VectorStoreServiceImpl.kt @@ -11,7 +11,9 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.BetaVectorStoreCreateParams @@ -35,143 +37,216 @@ class VectorStoreServiceImpl internal constructor(private val clientOptions: Cli private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: VectorStoreService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } private val files: FileService by lazy { FileServiceImpl(clientOptions) } private val fileBatches: FileBatchService by lazy { FileBatchServiceImpl(clientOptions) } + override fun withRawResponse(): VectorStoreService.WithRawResponse = withRawResponse + override fun files(): FileService = files override fun fileBatches(): FileBatchService = fileBatches - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Create a vector store. */ override fun create( params: BetaVectorStoreCreateParams, requestOptions: RequestOptions, - ): VectorStore { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("vector_stores") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): VectorStore = + // post /vector_stores + withRawResponse().create(params, requestOptions).parse() - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Retrieves a vector store. */ override fun retrieve( params: BetaVectorStoreRetrieveParams, requestOptions: RequestOptions, - ): VectorStore { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("vector_stores", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): VectorStore = + // get /vector_stores/{vector_store_id} + withRawResponse().retrieve(params, requestOptions).parse() - private val updateHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Modifies a vector store. */ override fun update( params: BetaVectorStoreUpdateParams, requestOptions: RequestOptions, - ): VectorStore { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("vector_stores", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { updateHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + ): VectorStore = + // post /vector_stores/{vector_store_id} + withRawResponse().update(params, requestOptions).parse() - /** Returns a list of vector stores. */ override fun list( params: BetaVectorStoreListParams, requestOptions: RequestOptions, - ): BetaVectorStoreListPage { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("vector_stores") - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - .let { BetaVectorStoreListPage.of(this, params, it) } - } - - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): BetaVectorStoreListPage = + // get /vector_stores + withRawResponse().list(params, requestOptions).parse() - /** Delete a vector store. */ override fun delete( params: BetaVectorStoreDeleteParams, requestOptions: RequestOptions, - ): VectorStoreDeleted { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments("vector_stores", params.getPathParam(0)) - .putAllHeaders(DEFAULT_HEADERS) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): VectorStoreDeleted = + // delete /vector_stores/{vector_store_id} + withRawResponse().delete(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + VectorStoreService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val files: FileService.WithRawResponse by lazy { + FileServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val fileBatches: FileBatchService.WithRawResponse by lazy { + FileBatchServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun files(): FileService.WithRawResponse = files + + override fun fileBatches(): FileBatchService.WithRawResponse = fileBatches + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: BetaVectorStoreCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("vector_stores") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaVectorStoreRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("vector_stores", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun update( + params: BetaVectorStoreUpdateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("vector_stores", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: BetaVectorStoreListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("vector_stores") + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + BetaVectorStoreListPage.of( + VectorStoreServiceImpl(clientOptions), + params, + it, + ) + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun delete( + params: BetaVectorStoreDeleteParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments("vector_stores", params.getPathParam(0)) + .putAllHeaders(DEFAULT_HEADERS) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/MessageService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/MessageService.kt index 5ab6766dc..1126cb803 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/MessageService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/MessageService.kt @@ -4,7 +4,9 @@ package com.openai.services.blocking.beta.threads +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.BetaThreadMessageCreateParams import com.openai.models.BetaThreadMessageDeleteParams import com.openai.models.BetaThreadMessageListPage @@ -16,6 +18,11 @@ import com.openai.models.MessageDeleted interface MessageService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** Create a message. */ @JvmOverloads fun create( @@ -50,4 +57,63 @@ interface MessageService { params: BetaThreadMessageDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): MessageDeleted + + /** A view of [MessageService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /threads/{thread_id}/messages`, but is otherwise + * the same as [MessageService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BetaThreadMessageCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /threads/{thread_id}/messages/{message_id}`, but is + * otherwise the same as [MessageService.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaThreadMessageRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /threads/{thread_id}/messages/{message_id}`, but is + * otherwise the same as [MessageService.update]. + */ + @JvmOverloads + @MustBeClosed + fun update( + params: BetaThreadMessageUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /threads/{thread_id}/messages`, but is otherwise the + * same as [MessageService.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: BetaThreadMessageListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `delete /threads/{thread_id}/messages/{message_id}`, but + * is otherwise the same as [MessageService.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: BetaThreadMessageDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/MessageServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/MessageServiceImpl.kt index a6690dcf7..1e697e0d1 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/MessageServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/MessageServiceImpl.kt @@ -11,7 +11,9 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.BetaThreadMessageCreateParams @@ -31,150 +33,207 @@ class MessageServiceImpl internal constructor(private val clientOptions: ClientO private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: MessageService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun withRawResponse(): MessageService.WithRawResponse = withRawResponse - /** Create a message. */ override fun create( params: BetaThreadMessageCreateParams, requestOptions: RequestOptions, - ): Message { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads", params.getPathParam(0), "messages") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): Message = + // post /threads/{thread_id}/messages + withRawResponse().create(params, requestOptions).parse() - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Retrieve a message. */ override fun retrieve( params: BetaThreadMessageRetrieveParams, requestOptions: RequestOptions, - ): Message { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments( - "threads", - params.getPathParam(0), - "messages", - params.getPathParam(1), - ) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - - private val updateHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): Message = + // get /threads/{thread_id}/messages/{message_id} + withRawResponse().retrieve(params, requestOptions).parse() - /** Modifies a message. */ override fun update( params: BetaThreadMessageUpdateParams, requestOptions: RequestOptions, - ): Message { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments( - "threads", - params.getPathParam(0), - "messages", - params.getPathParam(1), - ) - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { updateHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): Message = + // post /threads/{thread_id}/messages/{message_id} + withRawResponse().update(params, requestOptions).parse() - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) - - /** Returns a list of messages for a given thread. */ override fun list( params: BetaThreadMessageListParams, requestOptions: RequestOptions, - ): BetaThreadMessageListPage { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("threads", params.getPathParam(0), "messages") - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - .let { BetaThreadMessageListPage.of(this, params, it) } - } - - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): BetaThreadMessageListPage = + // get /threads/{thread_id}/messages + withRawResponse().list(params, requestOptions).parse() - /** Deletes a message. */ override fun delete( params: BetaThreadMessageDeleteParams, requestOptions: RequestOptions, - ): MessageDeleted { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments( - "threads", - params.getPathParam(0), - "messages", - params.getPathParam(1), - ) - .putAllHeaders(DEFAULT_HEADERS) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): MessageDeleted = + // delete /threads/{thread_id}/messages/{message_id} + withRawResponse().delete(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + MessageService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: BetaThreadMessageCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("threads", params.getPathParam(0), "messages") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaThreadMessageRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments( + "threads", + params.getPathParam(0), + "messages", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun update( + params: BetaThreadMessageUpdateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments( + "threads", + params.getPathParam(0), + "messages", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: BetaThreadMessageListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("threads", params.getPathParam(0), "messages") + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + BetaThreadMessageListPage.of(MessageServiceImpl(clientOptions), params, it) + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun delete( + params: BetaThreadMessageDeleteParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments( + "threads", + params.getPathParam(0), + "messages", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/RunService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/RunService.kt index 36854a6fb..f5c59290f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/RunService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/RunService.kt @@ -6,6 +6,7 @@ package com.openai.services.blocking.beta.threads import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.core.http.StreamResponse import com.openai.models.AssistantStreamEvent import com.openai.models.BetaThreadRunCancelParams @@ -20,6 +21,11 @@ import com.openai.services.blocking.beta.threads.runs.StepService interface RunService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun steps(): StepService /** Create a run. */ @@ -87,4 +93,100 @@ interface RunService { params: BetaThreadRunSubmitToolOutputsParams, requestOptions: RequestOptions = RequestOptions.none(), ): StreamResponse + + /** A view of [RunService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun steps(): StepService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /threads/{thread_id}/runs`, but is otherwise the + * same as [RunService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BetaThreadRunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /threads/{thread_id}/runs`, but is otherwise the + * same as [RunService.createStreaming]. + */ + @JvmOverloads + @MustBeClosed + fun createStreaming( + params: BetaThreadRunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> + + /** + * Returns a raw HTTP response for `get /threads/{thread_id}/runs/{run_id}`, but is + * otherwise the same as [RunService.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaThreadRunRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /threads/{thread_id}/runs/{run_id}`, but is + * otherwise the same as [RunService.update]. + */ + @JvmOverloads + @MustBeClosed + fun update( + params: BetaThreadRunUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /threads/{thread_id}/runs`, but is otherwise the + * same as [RunService.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: BetaThreadRunListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /threads/{thread_id}/runs/{run_id}/cancel`, but is + * otherwise the same as [RunService.cancel]. + */ + @JvmOverloads + @MustBeClosed + fun cancel( + params: BetaThreadRunCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post + * /threads/{thread_id}/runs/{run_id}/submit_tool_outputs`, but is otherwise the same as + * [RunService.submitToolOutputs]. + */ + @JvmOverloads + @MustBeClosed + fun submitToolOutputs( + params: BetaThreadRunSubmitToolOutputsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post + * /threads/{thread_id}/runs/{run_id}/submit_tool_outputs`, but is otherwise the same as + * [RunService.submitToolOutputsStreaming]. + */ + @JvmOverloads + @MustBeClosed + fun submitToolOutputsStreaming( + params: BetaThreadRunSubmitToolOutputsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/RunServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/RunServiceImpl.kt index 90091116f..836907a3d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/RunServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/RunServiceImpl.kt @@ -14,9 +14,11 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor import com.openai.core.http.StreamResponse +import com.openai.core.http.json import com.openai.core.http.map -import com.openai.core.json +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.AssistantStreamEvent @@ -38,260 +40,359 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: RunService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } private val steps: StepService by lazy { StepServiceImpl(clientOptions) } - override fun steps(): StepService = steps + override fun withRawResponse(): RunService.WithRawResponse = withRawResponse - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Create a run. */ - override fun create(params: BetaThreadRunCreateParams, requestOptions: RequestOptions): Run { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads", params.getPathParam(0), "runs") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, params.model().map { it.toString() }.orElse(null)) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + override fun steps(): StepService = steps - private val createStreamingHandler: Handler> = - sseHandler(clientOptions.jsonMapper) - .mapJson(includeEventAndData = true) - .withErrorHandler(errorHandler) + override fun create(params: BetaThreadRunCreateParams, requestOptions: RequestOptions): Run = + // post /threads/{thread_id}/runs + withRawResponse().create(params, requestOptions).parse() - /** Create a run. */ override fun createStreaming( params: BetaThreadRunCreateParams, requestOptions: RequestOptions, - ): StreamResponse { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads", params.getPathParam(0), "runs") - .putAllHeaders(DEFAULT_HEADERS) - .body( - json( - clientOptions.jsonMapper, - params - ._body() - .toBuilder() - .putAdditionalProperty("stream", JsonValue.from(true)) - .build(), - ) - ) - .build() - .prepare(clientOptions, params, params.model().map { it.toString() }.orElse(null)) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .let { createStreamingHandler.handle(it) } - .let { streamResponse -> - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - streamResponse.map { it.validate() } - } else { - streamResponse - } - } - } + ): StreamResponse = + // post /threads/{thread_id}/runs + withRawResponse().createStreaming(params, requestOptions).parse() - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Retrieves a run. */ override fun retrieve( params: BetaThreadRunRetrieveParams, requestOptions: RequestOptions, - ): Run { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("threads", params.getPathParam(0), "runs", params.getPathParam(1)) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): Run = + // get /threads/{thread_id}/runs/{run_id} + withRawResponse().retrieve(params, requestOptions).parse() - private val updateHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Modifies a run. */ - override fun update(params: BetaThreadRunUpdateParams, requestOptions: RequestOptions): Run { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("threads", params.getPathParam(0), "runs", params.getPathParam(1)) - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { updateHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + override fun update(params: BetaThreadRunUpdateParams, requestOptions: RequestOptions): Run = + // post /threads/{thread_id}/runs/{run_id} + withRawResponse().update(params, requestOptions).parse() - /** Returns a list of runs belonging to a thread. */ override fun list( params: BetaThreadRunListParams, requestOptions: RequestOptions, - ): BetaThreadRunListPage { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("threads", params.getPathParam(0), "runs") - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - .let { BetaThreadRunListPage.of(this, params, it) } - } - - private val cancelHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Cancels a run that is `in_progress`. */ - override fun cancel(params: BetaThreadRunCancelParams, requestOptions: RequestOptions): Run { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments( - "threads", - params.getPathParam(0), - "runs", - params.getPathParam(1), - "cancel", - ) - .putAllHeaders(DEFAULT_HEADERS) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { cancelHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): BetaThreadRunListPage = + // get /threads/{thread_id}/runs + withRawResponse().list(params, requestOptions).parse() - private val submitToolOutputsHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun cancel(params: BetaThreadRunCancelParams, requestOptions: RequestOptions): Run = + // post /threads/{thread_id}/runs/{run_id}/cancel + withRawResponse().cancel(params, requestOptions).parse() - /** - * When a run has the `status: "requires_action"` and `required_action.type` is - * `submit_tool_outputs`, this endpoint can be used to submit the outputs from the tool calls - * once they're all completed. All outputs must be submitted in a single request. - */ override fun submitToolOutputs( params: BetaThreadRunSubmitToolOutputsParams, requestOptions: RequestOptions, - ): Run { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments( - "threads", - params.getPathParam(0), - "runs", - params.getPathParam(1), - "submit_tool_outputs", - ) - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { submitToolOutputsHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - - private val submitToolOutputsStreamingHandler: Handler> = - sseHandler(clientOptions.jsonMapper) - .mapJson(includeEventAndData = true) - .withErrorHandler(errorHandler) + ): Run = + // post /threads/{thread_id}/runs/{run_id}/submit_tool_outputs + withRawResponse().submitToolOutputs(params, requestOptions).parse() - /** - * When a run has the `status: "requires_action"` and `required_action.type` is - * `submit_tool_outputs`, this endpoint can be used to submit the outputs from the tool calls - * once they're all completed. All outputs must be submitted in a single request. - */ override fun submitToolOutputsStreaming( params: BetaThreadRunSubmitToolOutputsParams, requestOptions: RequestOptions, - ): StreamResponse { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments( - "threads", - params.getPathParam(0), - "runs", - params.getPathParam(1), - "submit_tool_outputs", - ) - .putAllHeaders(DEFAULT_HEADERS) - .body( - json( - clientOptions.jsonMapper, - params - ._body() - .toBuilder() - .putAdditionalProperty("stream", JsonValue.from(true)) - .build(), + ): StreamResponse = + // post /threads/{thread_id}/runs/{run_id}/submit_tool_outputs + withRawResponse().submitToolOutputsStreaming(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + RunService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val steps: StepService.WithRawResponse by lazy { + StepServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun steps(): StepService.WithRawResponse = steps + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: BetaThreadRunCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("threads", params.getPathParam(0), "runs") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare( + clientOptions, + params, + params.model().map { it.toString() }.orElse(null), + ) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val createStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper) + .mapJson(includeEventAndData = true) + .withErrorHandler(errorHandler) + + override fun createStreaming( + params: BetaThreadRunCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("threads", params.getPathParam(0), "runs") + .putAllHeaders(DEFAULT_HEADERS) + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("stream", JsonValue.from(true)) + .build(), + ) + ) + .build() + .prepare( + clientOptions, + params, + params.model().map { it.toString() }.orElse(null), + ) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .let { createStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaThreadRunRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments( + "threads", + params.getPathParam(0), + "runs", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun update( + params: BetaThreadRunUpdateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments( + "threads", + params.getPathParam(0), + "runs", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: BetaThreadRunListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("threads", params.getPathParam(0), "runs") + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { BetaThreadRunListPage.of(RunServiceImpl(clientOptions), params, it) } + } + } + + private val cancelHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun cancel( + params: BetaThreadRunCancelParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments( + "threads", + params.getPathParam(0), + "runs", + params.getPathParam(1), + "cancel", + ) + .putAllHeaders(DEFAULT_HEADERS) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { cancelHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val submitToolOutputsHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun submitToolOutputs( + params: BetaThreadRunSubmitToolOutputsParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments( + "threads", + params.getPathParam(0), + "runs", + params.getPathParam(1), + "submit_tool_outputs", + ) + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { submitToolOutputsHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val submitToolOutputsStreamingHandler: + Handler> = + sseHandler(clientOptions.jsonMapper) + .mapJson(includeEventAndData = true) + .withErrorHandler(errorHandler) + + override fun submitToolOutputsStreaming( + params: BetaThreadRunSubmitToolOutputsParams, + requestOptions: RequestOptions, + ): HttpResponseFor> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments( + "threads", + params.getPathParam(0), + "runs", + params.getPathParam(1), + "submit_tool_outputs", + ) + .putAllHeaders(DEFAULT_HEADERS) + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("stream", JsonValue.from(true)) + .build(), + ) ) - ) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .let { submitToolOutputsStreamingHandler.handle(it) } - .let { streamResponse -> - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - streamResponse.map { it.validate() } - } else { - streamResponse - } + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .let { submitToolOutputsStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/runs/StepService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/runs/StepService.kt index a61bb90cd..97c8ff6de 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/runs/StepService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/runs/StepService.kt @@ -4,7 +4,9 @@ package com.openai.services.blocking.beta.threads.runs +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.BetaThreadRunStepListPage import com.openai.models.BetaThreadRunStepListParams import com.openai.models.BetaThreadRunStepRetrieveParams @@ -12,6 +14,11 @@ import com.openai.models.RunStep interface StepService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** Retrieves a run step. */ @JvmOverloads fun retrieve( @@ -25,4 +32,30 @@ interface StepService { params: BetaThreadRunStepListParams, requestOptions: RequestOptions = RequestOptions.none(), ): BetaThreadRunStepListPage + + /** A view of [StepService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `get /threads/{thread_id}/runs/{run_id}/steps/{step_id}`, + * but is otherwise the same as [StepService.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaThreadRunStepRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /threads/{thread_id}/runs/{run_id}/steps`, but is + * otherwise the same as [StepService.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: BetaThreadRunStepListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/runs/StepServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/runs/StepServiceImpl.kt index f3c73c311..bbb35d940 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/runs/StepServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/runs/StepServiceImpl.kt @@ -11,6 +11,8 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.BetaThreadRunStepListPage @@ -25,70 +27,100 @@ class StepServiceImpl internal constructor(private val clientOptions: ClientOpti private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: StepService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun withRawResponse(): StepService.WithRawResponse = withRawResponse - /** Retrieves a run step. */ override fun retrieve( params: BetaThreadRunStepRetrieveParams, requestOptions: RequestOptions, - ): RunStep { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments( - "threads", - params.getPathParam(0), - "runs", - params.getPathParam(1), - "steps", - params.getPathParam(2), - ) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): RunStep = + // get /threads/{thread_id}/runs/{run_id}/steps/{step_id} + withRawResponse().retrieve(params, requestOptions).parse() - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) - - /** Returns a list of run steps belonging to a run. */ override fun list( params: BetaThreadRunStepListParams, requestOptions: RequestOptions, - ): BetaThreadRunStepListPage { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments( - "threads", - params.getPathParam(0), - "runs", - params.getPathParam(1), - "steps", - ) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): BetaThreadRunStepListPage = + // get /threads/{thread_id}/runs/{run_id}/steps + withRawResponse().list(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + StepService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaThreadRunStepRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments( + "threads", + params.getPathParam(0), + "runs", + params.getPathParam(1), + "steps", + params.getPathParam(2), + ) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: BetaThreadRunStepListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments( + "threads", + params.getPathParam(0), + "runs", + params.getPathParam(1), + "steps", + ) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + BetaThreadRunStepListPage.of(StepServiceImpl(clientOptions), params, it) + } } - .let { BetaThreadRunStepListPage.of(this, params, it) } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileBatchService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileBatchService.kt index 4690bb6c2..690ae0d2a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileBatchService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileBatchService.kt @@ -4,7 +4,9 @@ package com.openai.services.blocking.beta.vectorStores +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.BetaVectorStoreFileBatchCancelParams import com.openai.models.BetaVectorStoreFileBatchCreateParams import com.openai.models.BetaVectorStoreFileBatchListFilesPage @@ -14,6 +16,11 @@ import com.openai.models.VectorStoreFileBatch interface FileBatchService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** Create a vector store file batch. */ @JvmOverloads fun create( @@ -44,4 +51,55 @@ interface FileBatchService { params: BetaVectorStoreFileBatchListFilesParams, requestOptions: RequestOptions = RequestOptions.none(), ): BetaVectorStoreFileBatchListFilesPage + + /** A view of [FileBatchService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /vector_stores/{vector_store_id}/file_batches`, but + * is otherwise the same as [FileBatchService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BetaVectorStoreFileBatchCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get + * /vector_stores/{vector_store_id}/file_batches/{batch_id}`, but is otherwise the same as + * [FileBatchService.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaVectorStoreFileBatchRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post + * /vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel`, but is otherwise the + * same as [FileBatchService.cancel]. + */ + @JvmOverloads + @MustBeClosed + fun cancel( + params: BetaVectorStoreFileBatchCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get + * /vector_stores/{vector_store_id}/file_batches/{batch_id}/files`, but is otherwise the + * same as [FileBatchService.listFiles]. + */ + @JvmOverloads + @MustBeClosed + fun listFiles( + params: BetaVectorStoreFileBatchListFilesParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileBatchServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileBatchServiceImpl.kt index 0ff7b2319..b55d47c5b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileBatchServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileBatchServiceImpl.kt @@ -11,7 +11,9 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.BetaVectorStoreFileBatchCancelParams @@ -29,129 +31,181 @@ class FileBatchServiceImpl internal constructor(private val clientOptions: Clien private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: FileBatchService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun withRawResponse(): FileBatchService.WithRawResponse = withRawResponse - /** Create a vector store file batch. */ override fun create( params: BetaVectorStoreFileBatchCreateParams, requestOptions: RequestOptions, - ): VectorStoreFileBatch { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("vector_stores", params.getPathParam(0), "file_batches") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): VectorStoreFileBatch = + // post /vector_stores/{vector_store_id}/file_batches + withRawResponse().create(params, requestOptions).parse() - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Retrieves a vector store file batch. */ override fun retrieve( params: BetaVectorStoreFileBatchRetrieveParams, requestOptions: RequestOptions, - ): VectorStoreFileBatch { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments( - "vector_stores", - params.getPathParam(0), - "file_batches", - params.getPathParam(1), - ) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): VectorStoreFileBatch = + // get /vector_stores/{vector_store_id}/file_batches/{batch_id} + withRawResponse().retrieve(params, requestOptions).parse() - private val cancelHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Cancel a vector store file batch. This attempts to cancel the processing of files in this - * batch as soon as possible. - */ override fun cancel( params: BetaVectorStoreFileBatchCancelParams, requestOptions: RequestOptions, - ): VectorStoreFileBatch { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments( - "vector_stores", - params.getPathParam(0), - "file_batches", - params.getPathParam(1), - "cancel", - ) - .putAllHeaders(DEFAULT_HEADERS) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { cancelHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): VectorStoreFileBatch = + // post /vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel + withRawResponse().cancel(params, requestOptions).parse() - private val listFilesHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) - - /** Returns a list of vector store files in a batch. */ override fun listFiles( params: BetaVectorStoreFileBatchListFilesParams, requestOptions: RequestOptions, - ): BetaVectorStoreFileBatchListFilesPage { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments( - "vector_stores", - params.getPathParam(0), - "file_batches", - params.getPathParam(1), - "files", - ) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { listFilesHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): BetaVectorStoreFileBatchListFilesPage = + // get /vector_stores/{vector_store_id}/file_batches/{batch_id}/files + withRawResponse().listFiles(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + FileBatchService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun create( + params: BetaVectorStoreFileBatchCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("vector_stores", params.getPathParam(0), "file_batches") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaVectorStoreFileBatchRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments( + "vector_stores", + params.getPathParam(0), + "file_batches", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val cancelHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun cancel( + params: BetaVectorStoreFileBatchCancelParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments( + "vector_stores", + params.getPathParam(0), + "file_batches", + params.getPathParam(1), + "cancel", + ) + .putAllHeaders(DEFAULT_HEADERS) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { cancelHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listFilesHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun listFiles( + params: BetaVectorStoreFileBatchListFilesParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments( + "vector_stores", + params.getPathParam(0), + "file_batches", + params.getPathParam(1), + "files", + ) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { listFilesHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + BetaVectorStoreFileBatchListFilesPage.of( + FileBatchServiceImpl(clientOptions), + params, + it, + ) + } } - .let { BetaVectorStoreFileBatchListFilesPage.of(this, params, it) } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileService.kt index 99c8c4add..887eab011 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileService.kt @@ -4,7 +4,9 @@ package com.openai.services.blocking.beta.vectorStores +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.BetaVectorStoreFileCreateParams import com.openai.models.BetaVectorStoreFileDeleteParams import com.openai.models.BetaVectorStoreFileListPage @@ -15,6 +17,11 @@ import com.openai.models.VectorStoreFileDeleted interface FileService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** * Create a vector store file by attaching a * [File](https://platform.openai.com/docs/api-reference/files) to a @@ -50,4 +57,53 @@ interface FileService { params: BetaVectorStoreFileDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): VectorStoreFileDeleted + + /** A view of [FileService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /vector_stores/{vector_store_id}/files`, but is + * otherwise the same as [FileService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: BetaVectorStoreFileCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /vector_stores/{vector_store_id}/files/{file_id}`, + * but is otherwise the same as [FileService.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: BetaVectorStoreFileRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /vector_stores/{vector_store_id}/files`, but is + * otherwise the same as [FileService.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: BetaVectorStoreFileListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `delete + * /vector_stores/{vector_store_id}/files/{file_id}`, but is otherwise the same as + * [FileService.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: BetaVectorStoreFileDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileServiceImpl.kt index 54fae9f21..e0a94d50a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/vectorStores/FileServiceImpl.kt @@ -11,7 +11,9 @@ import com.openai.core.http.Headers import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.BetaVectorStoreFileCreateParams @@ -29,127 +31,168 @@ class FileServiceImpl internal constructor(private val clientOptions: ClientOpti private val DEFAULT_HEADERS = Headers.builder().put("OpenAI-Beta", "assistants=v2").build() } - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: FileService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + override fun withRawResponse(): FileService.WithRawResponse = withRawResponse - /** - * Create a vector store file by attaching a - * [File](https://platform.openai.com/docs/api-reference/files) to a - * [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object). - */ override fun create( params: BetaVectorStoreFileCreateParams, requestOptions: RequestOptions, - ): VectorStoreFile { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("vector_stores", params.getPathParam(0), "files") - .putAllHeaders(DEFAULT_HEADERS) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): VectorStoreFile = + // post /vector_stores/{vector_store_id}/files + withRawResponse().create(params, requestOptions).parse() - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** Retrieves a vector store file. */ override fun retrieve( params: BetaVectorStoreFileRetrieveParams, requestOptions: RequestOptions, - ): VectorStoreFile { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments( - "vector_stores", - params.getPathParam(0), - "files", - params.getPathParam(1), - ) - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): VectorStoreFile = + // get /vector_stores/{vector_store_id}/files/{file_id} + withRawResponse().retrieve(params, requestOptions).parse() - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) - - /** Returns a list of vector store files. */ override fun list( params: BetaVectorStoreFileListParams, requestOptions: RequestOptions, - ): BetaVectorStoreFileListPage { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("vector_stores", params.getPathParam(0), "files") - .putAllHeaders(DEFAULT_HEADERS) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - .let { BetaVectorStoreFileListPage.of(this, params, it) } - } + ): BetaVectorStoreFileListPage = + // get /vector_stores/{vector_store_id}/files + withRawResponse().list(params, requestOptions).parse() - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Delete a vector store file. This will remove the file from the vector store but the file - * itself will not be deleted. To delete the file, use the - * [delete file](https://platform.openai.com/docs/api-reference/files/delete) endpoint. - */ override fun delete( params: BetaVectorStoreFileDeleteParams, requestOptions: RequestOptions, - ): VectorStoreFileDeleted { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments( - "vector_stores", - params.getPathParam(0), - "files", - params.getPathParam(1), - ) - .putAllHeaders(DEFAULT_HEADERS) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): VectorStoreFileDeleted = + // delete /vector_stores/{vector_store_id}/files/{file_id} + withRawResponse().delete(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + FileService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: BetaVectorStoreFileCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("vector_stores", params.getPathParam(0), "files") + .putAllHeaders(DEFAULT_HEADERS) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: BetaVectorStoreFileRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments( + "vector_stores", + params.getPathParam(0), + "files", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: BetaVectorStoreFileListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("vector_stores", params.getPathParam(0), "files") + .putAllHeaders(DEFAULT_HEADERS) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + BetaVectorStoreFileListPage.of(FileServiceImpl(clientOptions), params, it) + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun delete( + params: BetaVectorStoreFileDeleteParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments( + "vector_stores", + params.getPathParam(0), + "files", + params.getPathParam(1), + ) + .putAllHeaders(DEFAULT_HEADERS) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/CompletionService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/CompletionService.kt index 49230e272..fcb350084 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/CompletionService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/CompletionService.kt @@ -6,6 +6,7 @@ package com.openai.services.blocking.chat import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.core.http.StreamResponse import com.openai.models.ChatCompletion import com.openai.models.ChatCompletionChunk @@ -18,6 +19,11 @@ import com.openai.services.blocking.chat.completions.MessageService interface CompletionService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun messages(): MessageService /** @@ -85,4 +91,65 @@ interface CompletionService { params: ChatCompletionDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): ChatCompletionDeleted + + /** A view of [CompletionService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun messages(): MessageService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /chat/completions`, but is otherwise the same as + * [CompletionService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: ChatCompletionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /chat/completions`, but is otherwise the same as + * [CompletionService.createStreaming]. + */ + @JvmOverloads + @MustBeClosed + fun createStreaming( + params: ChatCompletionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> + + /** + * Returns a raw HTTP response for `get /chat/completions/{completion_id}`, but is otherwise + * the same as [CompletionService.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: ChatCompletionRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /chat/completions/{completion_id}`, but is + * otherwise the same as [CompletionService.update]. + */ + @JvmOverloads + @MustBeClosed + fun update( + params: ChatCompletionUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `delete /chat/completions/{completion_id}`, but is + * otherwise the same as [CompletionService.delete]. + */ + @JvmOverloads + @MustBeClosed + fun delete( + params: ChatCompletionDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/CompletionServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/CompletionServiceImpl.kt index 8246ceff5..48513218a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/CompletionServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/CompletionServiceImpl.kt @@ -13,9 +13,11 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor import com.openai.core.http.StreamResponse +import com.openai.core.http.json import com.openai.core.http.map -import com.openai.core.json +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.ChatCompletion @@ -31,176 +33,208 @@ import com.openai.services.blocking.chat.completions.MessageServiceImpl class CompletionServiceImpl internal constructor(private val clientOptions: ClientOptions) : CompletionService { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: CompletionService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } private val messages: MessageService by lazy { MessageServiceImpl(clientOptions) } + override fun withRawResponse(): CompletionService.WithRawResponse = withRawResponse + override fun messages(): MessageService = messages - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Creates a model response for the given chat conversation. Learn more in the - * [text generation](https://platform.openai.com/docs/guides/text-generation), - * [vision](https://platform.openai.com/docs/guides/vision), and - * [audio](https://platform.openai.com/docs/guides/audio) guides. - * - * Parameter support can differ depending on the model used to generate the response, - * particularly for newer reasoning models. Parameters that are only supported for reasoning - * models are noted below. For the current state of unsupported parameters in reasoning models, - * [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). - */ override fun create( params: ChatCompletionCreateParams, requestOptions: RequestOptions, - ): ChatCompletion { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("chat", "completions") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, params.model().toString()) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): ChatCompletion = + // post /chat/completions + withRawResponse().create(params, requestOptions).parse() - private val createStreamingHandler: Handler> = - sseHandler(clientOptions.jsonMapper) - .mapJson() - .withErrorHandler(errorHandler) - - /** - * Creates a model response for the given chat conversation. Learn more in the - * [text generation](https://platform.openai.com/docs/guides/text-generation), - * [vision](https://platform.openai.com/docs/guides/vision), and - * [audio](https://platform.openai.com/docs/guides/audio) guides. - * - * Parameter support can differ depending on the model used to generate the response, - * particularly for newer reasoning models. Parameters that are only supported for reasoning - * models are noted below. For the current state of unsupported parameters in reasoning models, - * [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). - */ override fun createStreaming( params: ChatCompletionCreateParams, requestOptions: RequestOptions, - ): StreamResponse { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("chat", "completions") - .body( - json( - clientOptions.jsonMapper, - params - ._body() - .toBuilder() - .putAdditionalProperty("stream", JsonValue.from(true)) - .build(), - ) - ) - .build() - .prepare(clientOptions, params, params.model().toString()) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .let { createStreamingHandler.handle(it) } - .let { streamResponse -> - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - streamResponse.map { it.validate() } - } else { - streamResponse - } - } - } - - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): StreamResponse = + // post /chat/completions + withRawResponse().createStreaming(params, requestOptions).parse() - /** - * Get a stored chat completion. Only chat completions that have been created with the `store` - * parameter set to `true` will be returned. - */ override fun retrieve( params: ChatCompletionRetrieveParams, requestOptions: RequestOptions, - ): ChatCompletion { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("chat", "completions", params.getPathParam(0)) - .build() - .prepare(clientOptions, params, null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): ChatCompletion = + // get /chat/completions/{completion_id} + withRawResponse().retrieve(params, requestOptions).parse() - private val updateHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Modify a stored chat completion. Only chat completions that have been created with the - * `store` parameter set to `true` can be modified. Currently, the only supported modification - * is to update the `metadata` field. - */ override fun update( params: ChatCompletionUpdateParams, requestOptions: RequestOptions, - ): ChatCompletion { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("chat", "completions", params.getPathParam(0)) - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { updateHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): ChatCompletion = + // post /chat/completions/{completion_id} + withRawResponse().update(params, requestOptions).parse() - private val deleteHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Delete a stored chat completion. Only chat completions that have been created with the - * `store` parameter set to `true` can be deleted. - */ override fun delete( params: ChatCompletionDeleteParams, requestOptions: RequestOptions, - ): ChatCompletionDeleted { - val request = - HttpRequest.builder() - .method(HttpMethod.DELETE) - .addPathSegments("chat", "completions", params.getPathParam(0)) - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepare(clientOptions, params, null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { deleteHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): ChatCompletionDeleted = + // delete /chat/completions/{completion_id} + withRawResponse().delete(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CompletionService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val messages: MessageService.WithRawResponse by lazy { + MessageServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun messages(): MessageService.WithRawResponse = messages + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: ChatCompletionCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("chat", "completions") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val createStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper) + .mapJson() + .withErrorHandler(errorHandler) + + override fun createStreaming( + params: ChatCompletionCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("chat", "completions") + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("stream", JsonValue.from(true)) + .build(), + ) + ) + .build() + .prepare(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .let { createStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: ChatCompletionRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("chat", "completions", params.getPathParam(0)) + .build() + .prepare(clientOptions, params, null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun update( + params: ChatCompletionUpdateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("chat", "completions", params.getPathParam(0)) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun delete( + params: ChatCompletionDeleteParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .addPathSegments("chat", "completions", params.getPathParam(0)) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params, null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/completions/MessageService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/completions/MessageService.kt index 562e6da92..fce7c1370 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/completions/MessageService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/completions/MessageService.kt @@ -4,12 +4,19 @@ package com.openai.services.blocking.chat.completions +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.ChatCompletionMessageListPage import com.openai.models.ChatCompletionMessageListParams interface MessageService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** * Get the messages in a stored chat completion. Only chat completions that have been created * with the `store` parameter set to `true` will be returned. @@ -19,4 +26,19 @@ interface MessageService { params: ChatCompletionMessageListParams, requestOptions: RequestOptions = RequestOptions.none(), ): ChatCompletionMessageListPage + + /** A view of [MessageService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `get /chat/completions/{completion_id}/messages`, but is + * otherwise the same as [MessageService.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: ChatCompletionMessageListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/completions/MessageServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/completions/MessageServiceImpl.kt index 870f51269..21029c965 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/completions/MessageServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/completions/MessageServiceImpl.kt @@ -10,6 +10,8 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.ChatCompletionMessageListPage @@ -18,34 +20,56 @@ import com.openai.models.ChatCompletionMessageListParams class MessageServiceImpl internal constructor(private val clientOptions: ClientOptions) : MessageService { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: MessageService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + override fun withRawResponse(): MessageService.WithRawResponse = withRawResponse - /** - * Get the messages in a stored chat completion. Only chat completions that have been created - * with the `store` parameter set to `true` will be returned. - */ override fun list( params: ChatCompletionMessageListParams, requestOptions: RequestOptions, - ): ChatCompletionMessageListPage { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("chat", "completions", params.getPathParam(0), "messages") - .build() - .prepare(clientOptions, params, null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): ChatCompletionMessageListPage = + // get /chat/completions/{completion_id}/messages + withRawResponse().list(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + MessageService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: ChatCompletionMessageListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("chat", "completions", params.getPathParam(0), "messages") + .build() + .prepare(clientOptions, params, null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + ChatCompletionMessageListPage.of( + MessageServiceImpl(clientOptions), + params, + it, + ) + } } - .let { ChatCompletionMessageListPage.of(this, params, it) } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/JobService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/JobService.kt index a66c54ed2..b2b92fd84 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/JobService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/JobService.kt @@ -4,7 +4,9 @@ package com.openai.services.blocking.fineTuning +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.FineTuningJob import com.openai.models.FineTuningJobCancelParams import com.openai.models.FineTuningJobCreateParams @@ -17,6 +19,11 @@ import com.openai.services.blocking.fineTuning.jobs.CheckpointService interface JobService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + fun checkpoints(): CheckpointService /** @@ -69,4 +76,73 @@ interface JobService { params: FineTuningJobListEventsParams, requestOptions: RequestOptions = RequestOptions.none(), ): FineTuningJobListEventsPage + + /** A view of [JobService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun checkpoints(): CheckpointService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /fine_tuning/jobs`, but is otherwise the same as + * [JobService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: FineTuningJobCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /fine_tuning/jobs/{fine_tuning_job_id}`, but is + * otherwise the same as [JobService.retrieve]. + */ + @JvmOverloads + @MustBeClosed + fun retrieve( + params: FineTuningJobRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /fine_tuning/jobs`, but is otherwise the same as + * [JobService.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: FineTuningJobListParams = FineTuningJobListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /fine_tuning/jobs`, but is otherwise the same as + * [JobService.list]. + */ + @MustBeClosed + fun list(requestOptions: RequestOptions): HttpResponseFor = + list(FineTuningJobListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /fine_tuning/jobs/{fine_tuning_job_id}/cancel`, but + * is otherwise the same as [JobService.cancel]. + */ + @JvmOverloads + @MustBeClosed + fun cancel( + params: FineTuningJobCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /fine_tuning/jobs/{fine_tuning_job_id}/events`, but + * is otherwise the same as [JobService.listEvents]. + */ + @JvmOverloads + @MustBeClosed + fun listEvents( + params: FineTuningJobListEventsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/JobServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/JobServiceImpl.kt index 6bc123cff..02c0d2429 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/JobServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/JobServiceImpl.kt @@ -10,7 +10,9 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler -import com.openai.core.json +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.json +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.FineTuningJob @@ -26,147 +28,198 @@ import com.openai.services.blocking.fineTuning.jobs.CheckpointServiceImpl class JobServiceImpl internal constructor(private val clientOptions: ClientOptions) : JobService { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: JobService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } private val checkpoints: CheckpointService by lazy { CheckpointServiceImpl(clientOptions) } + override fun withRawResponse(): JobService.WithRawResponse = withRawResponse + override fun checkpoints(): CheckpointService = checkpoints - private val createHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) - - /** - * Creates a fine-tuning job which begins the process of creating a new model from a given - * dataset. - * - * Response includes details of the enqueued job including job status and the name of the - * fine-tuned models once complete. - * - * [Learn more about fine-tuning](https://platform.openai.com/docs/guides/fine-tuning) - */ override fun create( params: FineTuningJobCreateParams, requestOptions: RequestOptions, - ): FineTuningJob { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("fine_tuning", "jobs") - .body(json(clientOptions.jsonMapper, params._body())) - .build() - .prepare(clientOptions, params, params.model().toString()) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { createHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - - private val retrieveHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): FineTuningJob = + // post /fine_tuning/jobs + withRawResponse().create(params, requestOptions).parse() - /** - * Get info about a fine-tuning job. - * - * [Learn more about fine-tuning](https://platform.openai.com/docs/guides/fine-tuning) - */ override fun retrieve( params: FineTuningJobRetrieveParams, requestOptions: RequestOptions, - ): FineTuningJob { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("fine_tuning", "jobs", params.getPathParam(0)) - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { retrieveHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } + ): FineTuningJob = + // get /fine_tuning/jobs/{fine_tuning_job_id} + withRawResponse().retrieve(params, requestOptions).parse() - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) - - /** List your organization's fine-tuning jobs */ override fun list( params: FineTuningJobListParams, requestOptions: RequestOptions, - ): FineTuningJobListPage { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("fine_tuning", "jobs") - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - .let { FineTuningJobListPage.of(this, params, it) } - } - - private val cancelHandler: Handler = - jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + ): FineTuningJobListPage = + // get /fine_tuning/jobs + withRawResponse().list(params, requestOptions).parse() - /** Immediately cancel a fine-tune job. */ override fun cancel( params: FineTuningJobCancelParams, requestOptions: RequestOptions, - ): FineTuningJob { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegments("fine_tuning", "jobs", params.getPathParam(0), "cancel") - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { cancelHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } - } - } - - private val listEventsHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + ): FineTuningJob = + // post /fine_tuning/jobs/{fine_tuning_job_id}/cancel + withRawResponse().cancel(params, requestOptions).parse() - /** Get status updates for a fine-tuning job. */ override fun listEvents( params: FineTuningJobListEventsParams, requestOptions: RequestOptions, - ): FineTuningJobListEventsPage { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("fine_tuning", "jobs", params.getPathParam(0), "events") - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { listEventsHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): FineTuningJobListEventsPage = + // get /fine_tuning/jobs/{fine_tuning_job_id}/events + withRawResponse().listEvents(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + JobService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val checkpoints: CheckpointService.WithRawResponse by lazy { + CheckpointServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun checkpoints(): CheckpointService.WithRawResponse = checkpoints + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: FineTuningJobCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("fine_tuning", "jobs") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, params.model().toString()) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun retrieve( + params: FineTuningJobRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("fine_tuning", "jobs", params.getPathParam(0)) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: FineTuningJobListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("fine_tuning", "jobs") + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { FineTuningJobListPage.of(JobServiceImpl(clientOptions), params, it) } + } + } + + private val cancelHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun cancel( + params: FineTuningJobCancelParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("fine_tuning", "jobs", params.getPathParam(0), "cancel") + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { cancelHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listEventsHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun listEvents( + params: FineTuningJobListEventsParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("fine_tuning", "jobs", params.getPathParam(0), "events") + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { listEventsHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + FineTuningJobListEventsPage.of(JobServiceImpl(clientOptions), params, it) + } } - .let { FineTuningJobListEventsPage.of(this, params, it) } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/jobs/CheckpointService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/jobs/CheckpointService.kt index ebc01058e..62d160528 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/jobs/CheckpointService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/jobs/CheckpointService.kt @@ -4,16 +4,38 @@ package com.openai.services.blocking.fineTuning.jobs +import com.google.errorprone.annotations.MustBeClosed import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor import com.openai.models.FineTuningJobCheckpointListPage import com.openai.models.FineTuningJobCheckpointListParams interface CheckpointService { + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + /** List checkpoints for a fine-tuning job. */ @JvmOverloads fun list( params: FineTuningJobCheckpointListParams, requestOptions: RequestOptions = RequestOptions.none(), ): FineTuningJobCheckpointListPage + + /** A view of [CheckpointService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `get /fine_tuning/jobs/{fine_tuning_job_id}/checkpoints`, + * but is otherwise the same as [CheckpointService.list]. + */ + @JvmOverloads + @MustBeClosed + fun list( + params: FineTuningJobCheckpointListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/jobs/CheckpointServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/jobs/CheckpointServiceImpl.kt index c62b24992..c4d1dcd2d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/jobs/CheckpointServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/fineTuning/jobs/CheckpointServiceImpl.kt @@ -10,6 +10,8 @@ import com.openai.core.handlers.withErrorHandler import com.openai.core.http.HttpMethod import com.openai.core.http.HttpRequest import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.parseable import com.openai.core.prepare import com.openai.errors.OpenAIError import com.openai.models.FineTuningJobCheckpointListPage @@ -18,31 +20,56 @@ import com.openai.models.FineTuningJobCheckpointListParams class CheckpointServiceImpl internal constructor(private val clientOptions: ClientOptions) : CheckpointService { - private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + private val withRawResponse: CheckpointService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } - private val listHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - .withErrorHandler(errorHandler) + override fun withRawResponse(): CheckpointService.WithRawResponse = withRawResponse - /** List checkpoints for a fine-tuning job. */ override fun list( params: FineTuningJobCheckpointListParams, requestOptions: RequestOptions, - ): FineTuningJobCheckpointListPage { - val request = - HttpRequest.builder() - .method(HttpMethod.GET) - .addPathSegments("fine_tuning", "jobs", params.getPathParam(0), "checkpoints") - .build() - .prepare(clientOptions, params, deploymentModel = null) - val response = clientOptions.httpClient.execute(request, requestOptions) - return response - .use { listHandler.handle(it) } - .also { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - it.validate() - } + ): FineTuningJobCheckpointListPage = + // get /fine_tuning/jobs/{fine_tuning_job_id}/checkpoints + withRawResponse().list(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CheckpointService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun list( + params: FineTuningJobCheckpointListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .addPathSegments("fine_tuning", "jobs", params.getPathParam(0), "checkpoints") + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + FineTuningJobCheckpointListPage.of( + CheckpointServiceImpl(clientOptions), + params, + it, + ) + } } - .let { FineTuningJobCheckpointListPage.of(this, params, it) } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/uploads/PartService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/uploads/PartService.kt index 35c68b2d6..894eaafb8 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/uploads/PartService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/uploads/PartService.kt @@ -1,5 +1,52 @@ // File generated from our OpenAPI spec by Stainless. +@file:Suppress("OVERLOADS_INTERFACE") // See https://youtrack.jetbrains.com/issue/KT-36102 + package com.openai.services.blocking.uploads -interface PartService +import com.google.errorprone.annotations.MustBeClosed +import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor +import com.openai.models.UploadPart +import com.openai.models.UploadPartCreateParams + +interface PartService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Adds a [Part](https://platform.openai.com/docs/api-reference/uploads/part-object) to an + * [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object. A Part + * represents a chunk of bytes from the file you are trying to upload. + * + * Each Part can be at most 64 MB, and you can add Parts until you hit the Upload maximum of 8 + * GB. + * + * It is possible to add multiple Parts in parallel. You can decide the intended order of the + * Parts when you + * [complete the Upload](https://platform.openai.com/docs/api-reference/uploads/complete). + */ + @JvmOverloads + fun create( + params: UploadPartCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): UploadPart + + /** A view of [PartService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /uploads/{upload_id}/parts`, but is otherwise the + * same as [PartService.create]. + */ + @JvmOverloads + @MustBeClosed + fun create( + params: UploadPartCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/uploads/PartServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/uploads/PartServiceImpl.kt index f49dc37ef..18f22faaf 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/uploads/PartServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/uploads/PartServiceImpl.kt @@ -3,5 +3,66 @@ package com.openai.services.blocking.uploads import com.openai.core.ClientOptions +import com.openai.core.RequestOptions +import com.openai.core.handlers.errorHandler +import com.openai.core.handlers.jsonHandler +import com.openai.core.handlers.withErrorHandler +import com.openai.core.http.HttpMethod +import com.openai.core.http.HttpRequest +import com.openai.core.http.HttpResponse.Handler +import com.openai.core.http.HttpResponseFor +import com.openai.core.http.multipartFormData +import com.openai.core.http.parseable +import com.openai.core.prepare +import com.openai.errors.OpenAIError +import com.openai.models.UploadPart +import com.openai.models.UploadPartCreateParams -class PartServiceImpl internal constructor(private val clientOptions: ClientOptions) : PartService +class PartServiceImpl internal constructor(private val clientOptions: ClientOptions) : PartService { + + private val withRawResponse: PartService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): PartService.WithRawResponse = withRawResponse + + override fun create( + params: UploadPartCreateParams, + requestOptions: RequestOptions, + ): UploadPart = + // post /uploads/{upload_id}/parts + withRawResponse().create(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + PartService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun create( + params: UploadPartCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("uploads", params.getPathParam(0), "parts") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params, deploymentModel = null) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response.parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/AudioSpeechCreateParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/AudioSpeechCreateParamsTest.kt new file mode 100644 index 000000000..5bd469f00 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/AudioSpeechCreateParamsTest.kt @@ -0,0 +1,59 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import kotlin.test.assertNotNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class AudioSpeechCreateParamsTest { + + @Test + fun create() { + AudioSpeechCreateParams.builder() + .input("input") + .model(SpeechModel.TTS_1) + .voice(AudioSpeechCreateParams.Voice.ALLOY) + .responseFormat(AudioSpeechCreateParams.ResponseFormat.MP3) + .speed(0.25) + .build() + } + + @Test + fun body() { + val params = + AudioSpeechCreateParams.builder() + .input("input") + .model(SpeechModel.TTS_1) + .voice(AudioSpeechCreateParams.Voice.ALLOY) + .responseFormat(AudioSpeechCreateParams.ResponseFormat.MP3) + .speed(0.25) + .build() + + val body = params._body() + + assertNotNull(body) + assertThat(body.input()).isEqualTo("input") + assertThat(body.model()).isEqualTo(SpeechModel.TTS_1) + assertThat(body.voice()).isEqualTo(AudioSpeechCreateParams.Voice.ALLOY) + assertThat(body.responseFormat()).contains(AudioSpeechCreateParams.ResponseFormat.MP3) + assertThat(body.speed()).contains(0.25) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + AudioSpeechCreateParams.builder() + .input("input") + .model(SpeechModel.TTS_1) + .voice(AudioSpeechCreateParams.Voice.ALLOY) + .build() + + val body = params._body() + + assertNotNull(body) + assertThat(body.input()).isEqualTo("input") + assertThat(body.model()).isEqualTo(SpeechModel.TTS_1) + assertThat(body.voice()).isEqualTo(AudioSpeechCreateParams.Voice.ALLOY) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/AudioTranscriptionCreateParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/AudioTranscriptionCreateParamsTest.kt new file mode 100644 index 000000000..8a6cd659e --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/AudioTranscriptionCreateParamsTest.kt @@ -0,0 +1,77 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.openai.core.MultipartField +import kotlin.test.assertNotNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class AudioTranscriptionCreateParamsTest { + + @Test + fun create() { + AudioTranscriptionCreateParams.builder() + .file("some content".toByteArray()) + .model(AudioModel.WHISPER_1) + .language("language") + .prompt("prompt") + .responseFormat(AudioResponseFormat.JSON) + .temperature(0.0) + .addTimestampGranularity(AudioTranscriptionCreateParams.TimestampGranularity.WORD) + .build() + } + + @Test + fun body() { + val params = + AudioTranscriptionCreateParams.builder() + .file("some content".toByteArray()) + .model(AudioModel.WHISPER_1) + .language("language") + .prompt("prompt") + .responseFormat(AudioResponseFormat.JSON) + .temperature(0.0) + .addTimestampGranularity(AudioTranscriptionCreateParams.TimestampGranularity.WORD) + .build() + + val body = params._body() + + assertNotNull(body) + assertThat(body.filterValues { !it.value.isNull() }) + .isEqualTo( + mapOf( + "file" to MultipartField.of("some content".toByteArray()), + "model" to MultipartField.of(AudioModel.WHISPER_1), + "language" to MultipartField.of("language"), + "prompt" to MultipartField.of("prompt"), + "response_format" to MultipartField.of(AudioResponseFormat.JSON), + "temperature" to MultipartField.of(0.0), + "timestamp_granularities" to + MultipartField.of( + listOf(AudioTranscriptionCreateParams.TimestampGranularity.WORD) + ), + ) + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + AudioTranscriptionCreateParams.builder() + .file("some content".toByteArray()) + .model(AudioModel.WHISPER_1) + .build() + + val body = params._body() + + assertNotNull(body) + assertThat(body.filterValues { !it.value.isNull() }) + .isEqualTo( + mapOf( + "file" to MultipartField.of("some content".toByteArray()), + "model" to MultipartField.of(AudioModel.WHISPER_1), + ) + ) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/AudioTranslationCreateParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/AudioTranslationCreateParamsTest.kt new file mode 100644 index 000000000..5f90d8d9b --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/AudioTranslationCreateParamsTest.kt @@ -0,0 +1,68 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.openai.core.MultipartField +import kotlin.test.assertNotNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class AudioTranslationCreateParamsTest { + + @Test + fun create() { + AudioTranslationCreateParams.builder() + .file("some content".toByteArray()) + .model(AudioModel.WHISPER_1) + .prompt("prompt") + .responseFormat(AudioResponseFormat.JSON) + .temperature(0.0) + .build() + } + + @Test + fun body() { + val params = + AudioTranslationCreateParams.builder() + .file("some content".toByteArray()) + .model(AudioModel.WHISPER_1) + .prompt("prompt") + .responseFormat(AudioResponseFormat.JSON) + .temperature(0.0) + .build() + + val body = params._body() + + assertNotNull(body) + assertThat(body.filterValues { !it.value.isNull() }) + .isEqualTo( + mapOf( + "file" to MultipartField.of("some content".toByteArray()), + "model" to MultipartField.of(AudioModel.WHISPER_1), + "prompt" to MultipartField.of("prompt"), + "response_format" to MultipartField.of(AudioResponseFormat.JSON), + "temperature" to MultipartField.of(0.0), + ) + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + AudioTranslationCreateParams.builder() + .file("some content".toByteArray()) + .model(AudioModel.WHISPER_1) + .build() + + val body = params._body() + + assertNotNull(body) + assertThat(body.filterValues { !it.value.isNull() }) + .isEqualTo( + mapOf( + "file" to MultipartField.of("some content".toByteArray()), + "model" to MultipartField.of(AudioModel.WHISPER_1), + ) + ) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/FileCreateParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/FileCreateParamsTest.kt new file mode 100644 index 000000000..926fdd2bb --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/FileCreateParamsTest.kt @@ -0,0 +1,59 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.openai.core.MultipartField +import kotlin.test.assertNotNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class FileCreateParamsTest { + + @Test + fun create() { + FileCreateParams.builder() + .file("some content".toByteArray()) + .purpose(FilePurpose.ASSISTANTS) + .build() + } + + @Test + fun body() { + val params = + FileCreateParams.builder() + .file("some content".toByteArray()) + .purpose(FilePurpose.ASSISTANTS) + .build() + + val body = params._body() + + assertNotNull(body) + assertThat(body.filterValues { !it.value.isNull() }) + .isEqualTo( + mapOf( + "file" to MultipartField.of("some content".toByteArray()), + "purpose" to MultipartField.of(FilePurpose.ASSISTANTS), + ) + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + FileCreateParams.builder() + .file("some content".toByteArray()) + .purpose(FilePurpose.ASSISTANTS) + .build() + + val body = params._body() + + assertNotNull(body) + assertThat(body.filterValues { !it.value.isNull() }) + .isEqualTo( + mapOf( + "file" to MultipartField.of("some content".toByteArray()), + "purpose" to MultipartField.of(FilePurpose.ASSISTANTS), + ) + ) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/ImageCreateVariationParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/ImageCreateVariationParamsTest.kt new file mode 100644 index 000000000..f616481f0 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/ImageCreateVariationParamsTest.kt @@ -0,0 +1,64 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.openai.core.MultipartField +import kotlin.test.assertNotNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class ImageCreateVariationParamsTest { + + @Test + fun create() { + ImageCreateVariationParams.builder() + .image("some content".toByteArray()) + .model(ImageModel.DALL_E_2) + .n(1L) + .responseFormat(ImageCreateVariationParams.ResponseFormat.URL) + .size(ImageCreateVariationParams.Size._256X256) + .user("user-1234") + .build() + } + + @Test + fun body() { + val params = + ImageCreateVariationParams.builder() + .image("some content".toByteArray()) + .model(ImageModel.DALL_E_2) + .n(1L) + .responseFormat(ImageCreateVariationParams.ResponseFormat.URL) + .size(ImageCreateVariationParams.Size._256X256) + .user("user-1234") + .build() + + val body = params._body() + + assertNotNull(body) + assertThat(body.filterValues { !it.value.isNull() }) + .isEqualTo( + mapOf( + "image" to MultipartField.of("some content".toByteArray()), + "model" to MultipartField.of(ImageModel.DALL_E_2), + "n" to MultipartField.of(1L), + "response_format" to + MultipartField.of(ImageCreateVariationParams.ResponseFormat.URL), + "size" to MultipartField.of(ImageCreateVariationParams.Size._256X256), + "user" to MultipartField.of("user-1234"), + ) + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + ImageCreateVariationParams.builder().image("some content".toByteArray()).build() + + val body = params._body() + + assertNotNull(body) + assertThat(body.filterValues { !it.value.isNull() }) + .isEqualTo(mapOf("image" to MultipartField.of("some content".toByteArray()))) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/ImageEditParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/ImageEditParamsTest.kt new file mode 100644 index 000000000..16655fce8 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/ImageEditParamsTest.kt @@ -0,0 +1,77 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.openai.core.MultipartField +import kotlin.test.assertNotNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class ImageEditParamsTest { + + @Test + fun create() { + ImageEditParams.builder() + .image("some content".toByteArray()) + .prompt("A cute baby sea otter wearing a beret") + .mask("some content".toByteArray()) + .model(ImageModel.DALL_E_2) + .n(1L) + .responseFormat(ImageEditParams.ResponseFormat.URL) + .size(ImageEditParams.Size._256X256) + .user("user-1234") + .build() + } + + @Test + fun body() { + val params = + ImageEditParams.builder() + .image("some content".toByteArray()) + .prompt("A cute baby sea otter wearing a beret") + .mask("some content".toByteArray()) + .model(ImageModel.DALL_E_2) + .n(1L) + .responseFormat(ImageEditParams.ResponseFormat.URL) + .size(ImageEditParams.Size._256X256) + .user("user-1234") + .build() + + val body = params._body() + + assertNotNull(body) + assertThat(body.filterValues { !it.value.isNull() }) + .isEqualTo( + mapOf( + "image" to MultipartField.of("some content".toByteArray()), + "prompt" to MultipartField.of("A cute baby sea otter wearing a beret"), + "mask" to MultipartField.of("some content".toByteArray()), + "model" to MultipartField.of(ImageModel.DALL_E_2), + "n" to MultipartField.of(1L), + "response_format" to MultipartField.of(ImageEditParams.ResponseFormat.URL), + "size" to MultipartField.of(ImageEditParams.Size._256X256), + "user" to MultipartField.of("user-1234"), + ) + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + ImageEditParams.builder() + .image("some content".toByteArray()) + .prompt("A cute baby sea otter wearing a beret") + .build() + + val body = params._body() + + assertNotNull(body) + assertThat(body.filterValues { !it.value.isNull() }) + .isEqualTo( + mapOf( + "image" to MultipartField.of("some content".toByteArray()), + "prompt" to MultipartField.of("A cute baby sea otter wearing a beret"), + ) + ) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/TranscriptionSegmentTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/TranscriptionSegmentTest.kt new file mode 100644 index 000000000..84959da57 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/TranscriptionSegmentTest.kt @@ -0,0 +1,37 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class TranscriptionSegmentTest { + + @Test + fun createTranscriptionSegment() { + val transcriptionSegment = + TranscriptionSegment.builder() + .id(0L) + .avgLogprob(0.0) + .compressionRatio(0.0) + .end(0.0) + .noSpeechProb(0.0) + .seek(0L) + .start(0.0) + .temperature(0.0) + .text("text") + .addToken(0L) + .build() + assertThat(transcriptionSegment).isNotNull + assertThat(transcriptionSegment.id()).isEqualTo(0L) + assertThat(transcriptionSegment.avgLogprob()).isEqualTo(0.0) + assertThat(transcriptionSegment.compressionRatio()).isEqualTo(0.0) + assertThat(transcriptionSegment.end()).isEqualTo(0.0) + assertThat(transcriptionSegment.noSpeechProb()).isEqualTo(0.0) + assertThat(transcriptionSegment.seek()).isEqualTo(0L) + assertThat(transcriptionSegment.start()).isEqualTo(0.0) + assertThat(transcriptionSegment.temperature()).isEqualTo(0.0) + assertThat(transcriptionSegment.text()).isEqualTo("text") + assertThat(transcriptionSegment.tokens()).containsExactly(0L) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/TranscriptionTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/TranscriptionTest.kt new file mode 100644 index 000000000..dd969fba9 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/TranscriptionTest.kt @@ -0,0 +1,16 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class TranscriptionTest { + + @Test + fun createTranscription() { + val transcription = Transcription.builder().text("text").build() + assertThat(transcription).isNotNull + assertThat(transcription.text()).isEqualTo("text") + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/TranscriptionVerboseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/TranscriptionVerboseTest.kt new file mode 100644 index 000000000..0a790c351 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/TranscriptionVerboseTest.kt @@ -0,0 +1,55 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class TranscriptionVerboseTest { + + @Test + fun createTranscriptionVerbose() { + val transcriptionVerbose = + TranscriptionVerbose.builder() + .duration(0.0) + .language("language") + .text("text") + .addSegment( + TranscriptionSegment.builder() + .id(0L) + .avgLogprob(0.0) + .compressionRatio(0.0) + .end(0.0) + .noSpeechProb(0.0) + .seek(0L) + .start(0.0) + .temperature(0.0) + .text("text") + .addToken(0L) + .build() + ) + .addWord(TranscriptionWord.builder().end(0.0).start(0.0).word("word").build()) + .build() + assertThat(transcriptionVerbose).isNotNull + assertThat(transcriptionVerbose.duration()).isEqualTo(0.0) + assertThat(transcriptionVerbose.language()).isEqualTo("language") + assertThat(transcriptionVerbose.text()).isEqualTo("text") + assertThat(transcriptionVerbose.segments().get()) + .containsExactly( + TranscriptionSegment.builder() + .id(0L) + .avgLogprob(0.0) + .compressionRatio(0.0) + .end(0.0) + .noSpeechProb(0.0) + .seek(0L) + .start(0.0) + .temperature(0.0) + .text("text") + .addToken(0L) + .build() + ) + assertThat(transcriptionVerbose.words().get()) + .containsExactly(TranscriptionWord.builder().end(0.0).start(0.0).word("word").build()) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/TranscriptionWordTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/TranscriptionWordTest.kt new file mode 100644 index 000000000..7da3df18f --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/TranscriptionWordTest.kt @@ -0,0 +1,18 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class TranscriptionWordTest { + + @Test + fun createTranscriptionWord() { + val transcriptionWord = TranscriptionWord.builder().end(0.0).start(0.0).word("word").build() + assertThat(transcriptionWord).isNotNull + assertThat(transcriptionWord.end()).isEqualTo(0.0) + assertThat(transcriptionWord.start()).isEqualTo(0.0) + assertThat(transcriptionWord.word()).isEqualTo("word") + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/TranslationTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/TranslationTest.kt new file mode 100644 index 000000000..831a072e4 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/TranslationTest.kt @@ -0,0 +1,16 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class TranslationTest { + + @Test + fun createTranslation() { + val translation = Translation.builder().text("text").build() + assertThat(translation).isNotNull + assertThat(translation.text()).isEqualTo("text") + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/TranslationVerboseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/TranslationVerboseTest.kt new file mode 100644 index 000000000..a7d324798 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/TranslationVerboseTest.kt @@ -0,0 +1,52 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class TranslationVerboseTest { + + @Test + fun createTranslationVerbose() { + val translationVerbose = + TranslationVerbose.builder() + .duration(0.0) + .language("language") + .text("text") + .addSegment( + TranscriptionSegment.builder() + .id(0L) + .avgLogprob(0.0) + .compressionRatio(0.0) + .end(0.0) + .noSpeechProb(0.0) + .seek(0L) + .start(0.0) + .temperature(0.0) + .text("text") + .addToken(0L) + .build() + ) + .build() + assertThat(translationVerbose).isNotNull + assertThat(translationVerbose.duration()).isEqualTo(0.0) + assertThat(translationVerbose.language()).isEqualTo("language") + assertThat(translationVerbose.text()).isEqualTo("text") + assertThat(translationVerbose.segments().get()) + .containsExactly( + TranscriptionSegment.builder() + .id(0L) + .avgLogprob(0.0) + .compressionRatio(0.0) + .end(0.0) + .noSpeechProb(0.0) + .seek(0L) + .start(0.0) + .temperature(0.0) + .text("text") + .addToken(0L) + .build() + ) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/UploadPartCreateParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/UploadPartCreateParamsTest.kt new file mode 100644 index 000000000..40297a6a6 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/UploadPartCreateParamsTest.kt @@ -0,0 +1,63 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models + +import com.openai.core.MultipartField +import kotlin.test.assertNotNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class UploadPartCreateParamsTest { + + @Test + fun create() { + UploadPartCreateParams.builder() + .uploadId("upload_abc123") + .data("some content".toByteArray()) + .build() + } + + @Test + fun body() { + val params = + UploadPartCreateParams.builder() + .uploadId("upload_abc123") + .data("some content".toByteArray()) + .build() + + val body = params._body() + + assertNotNull(body) + assertThat(body.filterValues { !it.value.isNull() }) + .isEqualTo(mapOf("data" to MultipartField.of("some content".toByteArray()))) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + UploadPartCreateParams.builder() + .uploadId("upload_abc123") + .data("some content".toByteArray()) + .build() + + val body = params._body() + + assertNotNull(body) + assertThat(body.filterValues { !it.value.isNull() }) + .isEqualTo(mapOf("data" to MultipartField.of("some content".toByteArray()))) + } + + @Test + fun getPathParam() { + val params = + UploadPartCreateParams.builder() + .uploadId("upload_abc123") + .data("some content".toByteArray()) + .build() + assertThat(params).isNotNull + // path param "uploadId" + assertThat(params.getPathParam(0)).isEqualTo("upload_abc123") + // out-of-bound path param + assertThat(params.getPathParam(1)).isEqualTo("") + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/services/ErrorHandlingTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/ErrorHandlingTest.kt index 0a2d4938d..4aa3e4a63 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/ErrorHandlingTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/ErrorHandlingTest.kt @@ -2,9 +2,7 @@ package com.openai.services -import com.fasterxml.jackson.databind.json.JsonMapper import com.github.tomakehurst.wiremock.client.WireMock.anyUrl -import com.github.tomakehurst.wiremock.client.WireMock.ok import com.github.tomakehurst.wiremock.client.WireMock.post import com.github.tomakehurst.wiremock.client.WireMock.status import com.github.tomakehurst.wiremock.client.WireMock.stubFor @@ -25,23 +23,31 @@ import com.openai.errors.RateLimitException import com.openai.errors.UnauthorizedException import com.openai.errors.UnexpectedStatusCodeException import com.openai.errors.UnprocessableEntityException -import com.openai.models.FineTuningJob import com.openai.models.FineTuningJobCreateParams -import com.openai.models.FineTuningJobWandbIntegration -import com.openai.models.FineTuningJobWandbIntegrationObject import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatThrownBy -import org.assertj.core.api.InstanceOfAssertFactories +import org.assertj.core.api.Assertions.entry import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows @WireMockTest class ErrorHandlingTest { - private val JSON_MAPPER: JsonMapper = jsonMapper() + companion object { - private val OPENAI_ERROR: OpenAIError = - OpenAIError.builder().putAdditionalProperty("key", JsonValue.from("value")).build() + private val ERROR: OpenAIError = + OpenAIError.builder() + .putAdditionalProperty("errorProperty", JsonValue.from("42")) + .build() + + private val ERROR_JSON: ByteArray = jsonMapper().writeValueAsBytes(ERROR) + + private const val HEADER_NAME: String = "Error-Header" + + private const val HEADER_VALUE: String = "42" + + private const val NOT_JSON: String = "Not JSON" + } private lateinit var client: OpenAIClient @@ -49,950 +55,691 @@ class ErrorHandlingTest { fun beforeEach(wmRuntimeInfo: WireMockRuntimeInfo) { client = OpenAIOkHttpClient.builder() - .baseUrl(wmRuntimeInfo.getHttpBaseUrl()) + .baseUrl(wmRuntimeInfo.httpBaseUrl) .apiKey("My API Key") - .organization("My Organization") - .project("My Project") .build() } @Test - fun jobsCreate200() { - val params = - FineTuningJobCreateParams.builder() - .model(FineTuningJobCreateParams.Model.BABBAGE_002) - .trainingFile("file-abc123") - .hyperparameters( - FineTuningJobCreateParams.Hyperparameters.builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .addIntegration( - FineTuningJobCreateParams.Integration.builder() - .wandb( - FineTuningJobCreateParams.Integration.Wandb.builder() - .project("my-wandb-project") - .entity("entity") - .name("name") - .addTag("custom-tag") - .build() - ) - .build() - ) - .method( - FineTuningJobCreateParams.Method.builder() - .dpo( - FineTuningJobCreateParams.Method.Dpo.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Dpo.Hyperparameters.builder() - .batchSizeAuto() - .betaAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .build() - ) - .supervised( - FineTuningJobCreateParams.Method.Supervised.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Supervised.Hyperparameters - .builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + fun jobsCreate400() { + val jobService = client.fineTuning().jobs() + stubFor( + post(anyUrl()) + .willReturn(status(400).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON)) + ) + + val e = + assertThrows { + jobService.create( + FineTuningJobCreateParams.builder() + .model(FineTuningJobCreateParams.Model.BABBAGE_002) + .trainingFile("file-abc123") + .hyperparameters( + FineTuningJobCreateParams.Hyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .addIntegration( + FineTuningJobCreateParams.Integration.builder() + .wandb( + FineTuningJobCreateParams.Integration.Wandb.builder() + .project("my-wandb-project") + .entity("entity") + .name("name") + .addTag("custom-tag") .build() ) .build() ) - .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) - .build() - ) - .seed(42L) - .suffix("x") - .validationFile("file-abc123") - .build() - - val expected = - FineTuningJob.builder() - .id("id") - .createdAt(0L) - .error( - FineTuningJob.Error.builder() - .code("code") - .message("message") - .param("param") - .build() - ) - .fineTunedModel("fine_tuned_model") - .finishedAt(0L) - .hyperparameters( - FineTuningJob.Hyperparameters.builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .model("model") - .organizationId("organization_id") - .addResultFile("file-abc123") - .seed(0L) - .status(FineTuningJob.Status.VALIDATING_FILES) - .trainedTokens(0L) - .trainingFile("training_file") - .validationFile("validation_file") - .estimatedFinish(0L) - .addIntegration( - FineTuningJobWandbIntegrationObject.builder() - .wandb( - FineTuningJobWandbIntegration.builder() - .project("my-wandb-project") - .entity("entity") - .name("name") - .addTag("custom-tag") - .build() - ) - .build() - ) - .method( - FineTuningJob.Method.builder() - .dpo( - FineTuningJob.Method.Dpo.builder() - .hyperparameters( - FineTuningJob.Method.Dpo.Hyperparameters.builder() - .batchSizeAuto() - .betaAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + .method( + FineTuningJobCreateParams.Method.builder() + .dpo( + FineTuningJobCreateParams.Method.Dpo.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Dpo.Hyperparameters + .builder() + .batchSizeAuto() + .betaAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) .build() ) - .build() - ) - .supervised( - FineTuningJob.Method.Supervised.builder() - .hyperparameters( - FineTuningJob.Method.Supervised.Hyperparameters.builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + .supervised( + FineTuningJobCreateParams.Method.Supervised.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Supervised + .Hyperparameters + .builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) .build() ) + .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) .build() ) - .type(FineTuningJob.Method.Type.SUPERVISED) + .seed(42L) + .suffix("x") + .validationFile("file-abc123") .build() ) - .build() - - stubFor(post(anyUrl()).willReturn(ok().withBody(toJson(expected)))) + } - assertThat(client.fineTuning().jobs().create(params)).isEqualTo(expected) + assertThat(e.statusCode()).isEqualTo(400) + assertThat(e.error()).isEqualTo(ERROR) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) } @Test - fun jobsCreate400() { - val params = - FineTuningJobCreateParams.builder() - .model(FineTuningJobCreateParams.Model.BABBAGE_002) - .trainingFile("file-abc123") - .hyperparameters( - FineTuningJobCreateParams.Hyperparameters.builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .addIntegration( - FineTuningJobCreateParams.Integration.builder() - .wandb( - FineTuningJobCreateParams.Integration.Wandb.builder() - .project("my-wandb-project") - .entity("entity") - .name("name") - .addTag("custom-tag") - .build() - ) - .build() - ) - .method( - FineTuningJobCreateParams.Method.builder() - .dpo( - FineTuningJobCreateParams.Method.Dpo.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Dpo.Hyperparameters.builder() - .batchSizeAuto() - .betaAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .build() - ) - .supervised( - FineTuningJobCreateParams.Method.Supervised.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Supervised.Hyperparameters - .builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .build() - ) - .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) - .build() - ) - .seed(42L) - .suffix("x") - .validationFile("file-abc123") - .build() - + fun jobsCreate401() { + val jobService = client.fineTuning().jobs() stubFor( post(anyUrl()) - .willReturn(status(400).withHeader("Foo", "Bar").withBody(toJson(OPENAI_ERROR))) + .willReturn(status(401).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON)) ) - assertThatThrownBy({ client.fineTuning().jobs().create(params) }) - .satisfies({ e -> - assertBadRequest(e, Headers.builder().put("Foo", "Bar").build(), OPENAI_ERROR) - }) - } - - @Test - fun jobsCreate401() { - val params = - FineTuningJobCreateParams.builder() - .model(FineTuningJobCreateParams.Model.BABBAGE_002) - .trainingFile("file-abc123") - .hyperparameters( - FineTuningJobCreateParams.Hyperparameters.builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .addIntegration( - FineTuningJobCreateParams.Integration.builder() - .wandb( - FineTuningJobCreateParams.Integration.Wandb.builder() - .project("my-wandb-project") - .entity("entity") - .name("name") - .addTag("custom-tag") - .build() - ) - .build() - ) - .method( - FineTuningJobCreateParams.Method.builder() - .dpo( - FineTuningJobCreateParams.Method.Dpo.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Dpo.Hyperparameters.builder() - .batchSizeAuto() - .betaAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + val e = + assertThrows { + jobService.create( + FineTuningJobCreateParams.builder() + .model(FineTuningJobCreateParams.Model.BABBAGE_002) + .trainingFile("file-abc123") + .hyperparameters( + FineTuningJobCreateParams.Hyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .addIntegration( + FineTuningJobCreateParams.Integration.builder() + .wandb( + FineTuningJobCreateParams.Integration.Wandb.builder() + .project("my-wandb-project") + .entity("entity") + .name("name") + .addTag("custom-tag") .build() ) .build() ) - .supervised( - FineTuningJobCreateParams.Method.Supervised.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Supervised.Hyperparameters - .builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + .method( + FineTuningJobCreateParams.Method.builder() + .dpo( + FineTuningJobCreateParams.Method.Dpo.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Dpo.Hyperparameters + .builder() + .batchSizeAuto() + .betaAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) .build() ) + .supervised( + FineTuningJobCreateParams.Method.Supervised.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Supervised + .Hyperparameters + .builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .build() + ) + .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) .build() ) - .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) + .seed(42L) + .suffix("x") + .validationFile("file-abc123") .build() ) - .seed(42L) - .suffix("x") - .validationFile("file-abc123") - .build() + } - stubFor( - post(anyUrl()) - .willReturn(status(401).withHeader("Foo", "Bar").withBody(toJson(OPENAI_ERROR))) - ) - - assertThatThrownBy({ client.fineTuning().jobs().create(params) }) - .satisfies({ e -> - assertUnauthorized(e, Headers.builder().put("Foo", "Bar").build(), OPENAI_ERROR) - }) + assertThat(e.statusCode()).isEqualTo(401) + assertThat(e.error()).isEqualTo(ERROR) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) } @Test fun jobsCreate403() { - val params = - FineTuningJobCreateParams.builder() - .model(FineTuningJobCreateParams.Model.BABBAGE_002) - .trainingFile("file-abc123") - .hyperparameters( - FineTuningJobCreateParams.Hyperparameters.builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .addIntegration( - FineTuningJobCreateParams.Integration.builder() - .wandb( - FineTuningJobCreateParams.Integration.Wandb.builder() - .project("my-wandb-project") - .entity("entity") - .name("name") - .addTag("custom-tag") - .build() - ) - .build() - ) - .method( - FineTuningJobCreateParams.Method.builder() - .dpo( - FineTuningJobCreateParams.Method.Dpo.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Dpo.Hyperparameters.builder() - .batchSizeAuto() - .betaAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .build() - ) - .supervised( - FineTuningJobCreateParams.Method.Supervised.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Supervised.Hyperparameters - .builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .build() - ) - .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) - .build() - ) - .seed(42L) - .suffix("x") - .validationFile("file-abc123") - .build() - + val jobService = client.fineTuning().jobs() stubFor( post(anyUrl()) - .willReturn(status(403).withHeader("Foo", "Bar").withBody(toJson(OPENAI_ERROR))) + .willReturn(status(403).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON)) ) - assertThatThrownBy({ client.fineTuning().jobs().create(params) }) - .satisfies({ e -> - assertPermissionDenied(e, Headers.builder().put("Foo", "Bar").build(), OPENAI_ERROR) - }) - } - - @Test - fun jobsCreate404() { - val params = - FineTuningJobCreateParams.builder() - .model(FineTuningJobCreateParams.Model.BABBAGE_002) - .trainingFile("file-abc123") - .hyperparameters( - FineTuningJobCreateParams.Hyperparameters.builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .addIntegration( - FineTuningJobCreateParams.Integration.builder() - .wandb( - FineTuningJobCreateParams.Integration.Wandb.builder() - .project("my-wandb-project") - .entity("entity") - .name("name") - .addTag("custom-tag") - .build() - ) - .build() - ) - .method( - FineTuningJobCreateParams.Method.builder() - .dpo( - FineTuningJobCreateParams.Method.Dpo.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Dpo.Hyperparameters.builder() - .batchSizeAuto() - .betaAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + val e = + assertThrows { + jobService.create( + FineTuningJobCreateParams.builder() + .model(FineTuningJobCreateParams.Model.BABBAGE_002) + .trainingFile("file-abc123") + .hyperparameters( + FineTuningJobCreateParams.Hyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .addIntegration( + FineTuningJobCreateParams.Integration.builder() + .wandb( + FineTuningJobCreateParams.Integration.Wandb.builder() + .project("my-wandb-project") + .entity("entity") + .name("name") + .addTag("custom-tag") .build() ) .build() ) - .supervised( - FineTuningJobCreateParams.Method.Supervised.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Supervised.Hyperparameters - .builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + .method( + FineTuningJobCreateParams.Method.builder() + .dpo( + FineTuningJobCreateParams.Method.Dpo.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Dpo.Hyperparameters + .builder() + .batchSizeAuto() + .betaAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) .build() ) + .supervised( + FineTuningJobCreateParams.Method.Supervised.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Supervised + .Hyperparameters + .builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .build() + ) + .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) .build() ) - .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) + .seed(42L) + .suffix("x") + .validationFile("file-abc123") .build() ) - .seed(42L) - .suffix("x") - .validationFile("file-abc123") - .build() + } + assertThat(e.statusCode()).isEqualTo(403) + assertThat(e.error()).isEqualTo(ERROR) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + } + + @Test + fun jobsCreate404() { + val jobService = client.fineTuning().jobs() stubFor( post(anyUrl()) - .willReturn(status(404).withHeader("Foo", "Bar").withBody(toJson(OPENAI_ERROR))) + .willReturn(status(404).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON)) ) - assertThatThrownBy({ client.fineTuning().jobs().create(params) }) - .satisfies({ e -> - assertNotFound(e, Headers.builder().put("Foo", "Bar").build(), OPENAI_ERROR) - }) - } - - @Test - fun jobsCreate422() { - val params = - FineTuningJobCreateParams.builder() - .model(FineTuningJobCreateParams.Model.BABBAGE_002) - .trainingFile("file-abc123") - .hyperparameters( - FineTuningJobCreateParams.Hyperparameters.builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .addIntegration( - FineTuningJobCreateParams.Integration.builder() - .wandb( - FineTuningJobCreateParams.Integration.Wandb.builder() - .project("my-wandb-project") - .entity("entity") - .name("name") - .addTag("custom-tag") - .build() - ) - .build() - ) - .method( - FineTuningJobCreateParams.Method.builder() - .dpo( - FineTuningJobCreateParams.Method.Dpo.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Dpo.Hyperparameters.builder() - .batchSizeAuto() - .betaAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + val e = + assertThrows { + jobService.create( + FineTuningJobCreateParams.builder() + .model(FineTuningJobCreateParams.Model.BABBAGE_002) + .trainingFile("file-abc123") + .hyperparameters( + FineTuningJobCreateParams.Hyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .addIntegration( + FineTuningJobCreateParams.Integration.builder() + .wandb( + FineTuningJobCreateParams.Integration.Wandb.builder() + .project("my-wandb-project") + .entity("entity") + .name("name") + .addTag("custom-tag") .build() ) .build() ) - .supervised( - FineTuningJobCreateParams.Method.Supervised.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Supervised.Hyperparameters - .builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + .method( + FineTuningJobCreateParams.Method.builder() + .dpo( + FineTuningJobCreateParams.Method.Dpo.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Dpo.Hyperparameters + .builder() + .batchSizeAuto() + .betaAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .build() + ) + .supervised( + FineTuningJobCreateParams.Method.Supervised.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Supervised + .Hyperparameters + .builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) .build() ) + .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) .build() ) - .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) + .seed(42L) + .suffix("x") + .validationFile("file-abc123") .build() ) - .seed(42L) - .suffix("x") - .validationFile("file-abc123") - .build() + } + assertThat(e.statusCode()).isEqualTo(404) + assertThat(e.error()).isEqualTo(ERROR) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + } + + @Test + fun jobsCreate422() { + val jobService = client.fineTuning().jobs() stubFor( post(anyUrl()) - .willReturn(status(422).withHeader("Foo", "Bar").withBody(toJson(OPENAI_ERROR))) + .willReturn(status(422).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON)) ) - assertThatThrownBy({ client.fineTuning().jobs().create(params) }) - .satisfies({ e -> - assertUnprocessableEntity( - e, - Headers.builder().put("Foo", "Bar").build(), - OPENAI_ERROR, - ) - }) - } - - @Test - fun jobsCreate429() { - val params = - FineTuningJobCreateParams.builder() - .model(FineTuningJobCreateParams.Model.BABBAGE_002) - .trainingFile("file-abc123") - .hyperparameters( - FineTuningJobCreateParams.Hyperparameters.builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .addIntegration( - FineTuningJobCreateParams.Integration.builder() - .wandb( - FineTuningJobCreateParams.Integration.Wandb.builder() - .project("my-wandb-project") - .entity("entity") - .name("name") - .addTag("custom-tag") - .build() - ) - .build() - ) - .method( - FineTuningJobCreateParams.Method.builder() - .dpo( - FineTuningJobCreateParams.Method.Dpo.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Dpo.Hyperparameters.builder() - .batchSizeAuto() - .betaAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + val e = + assertThrows { + jobService.create( + FineTuningJobCreateParams.builder() + .model(FineTuningJobCreateParams.Model.BABBAGE_002) + .trainingFile("file-abc123") + .hyperparameters( + FineTuningJobCreateParams.Hyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .addIntegration( + FineTuningJobCreateParams.Integration.builder() + .wandb( + FineTuningJobCreateParams.Integration.Wandb.builder() + .project("my-wandb-project") + .entity("entity") + .name("name") + .addTag("custom-tag") .build() ) .build() ) - .supervised( - FineTuningJobCreateParams.Method.Supervised.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Supervised.Hyperparameters - .builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + .method( + FineTuningJobCreateParams.Method.builder() + .dpo( + FineTuningJobCreateParams.Method.Dpo.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Dpo.Hyperparameters + .builder() + .batchSizeAuto() + .betaAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .build() + ) + .supervised( + FineTuningJobCreateParams.Method.Supervised.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Supervised + .Hyperparameters + .builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) .build() ) + .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) .build() ) - .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) + .seed(42L) + .suffix("x") + .validationFile("file-abc123") .build() ) - .seed(42L) - .suffix("x") - .validationFile("file-abc123") - .build() + } + + assertThat(e.statusCode()).isEqualTo(422) + assertThat(e.error()).isEqualTo(ERROR) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + } + @Test + fun jobsCreate429() { + val jobService = client.fineTuning().jobs() stubFor( post(anyUrl()) - .willReturn(status(429).withHeader("Foo", "Bar").withBody(toJson(OPENAI_ERROR))) + .willReturn(status(429).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON)) ) - assertThatThrownBy({ client.fineTuning().jobs().create(params) }) - .satisfies({ e -> - assertRateLimit(e, Headers.builder().put("Foo", "Bar").build(), OPENAI_ERROR) - }) - } - - @Test - fun jobsCreate500() { - val params = - FineTuningJobCreateParams.builder() - .model(FineTuningJobCreateParams.Model.BABBAGE_002) - .trainingFile("file-abc123") - .hyperparameters( - FineTuningJobCreateParams.Hyperparameters.builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .addIntegration( - FineTuningJobCreateParams.Integration.builder() - .wandb( - FineTuningJobCreateParams.Integration.Wandb.builder() - .project("my-wandb-project") - .entity("entity") - .name("name") - .addTag("custom-tag") - .build() - ) - .build() - ) - .method( - FineTuningJobCreateParams.Method.builder() - .dpo( - FineTuningJobCreateParams.Method.Dpo.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Dpo.Hyperparameters.builder() - .batchSizeAuto() - .betaAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + val e = + assertThrows { + jobService.create( + FineTuningJobCreateParams.builder() + .model(FineTuningJobCreateParams.Model.BABBAGE_002) + .trainingFile("file-abc123") + .hyperparameters( + FineTuningJobCreateParams.Hyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .addIntegration( + FineTuningJobCreateParams.Integration.builder() + .wandb( + FineTuningJobCreateParams.Integration.Wandb.builder() + .project("my-wandb-project") + .entity("entity") + .name("name") + .addTag("custom-tag") .build() ) .build() ) - .supervised( - FineTuningJobCreateParams.Method.Supervised.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Supervised.Hyperparameters - .builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + .method( + FineTuningJobCreateParams.Method.builder() + .dpo( + FineTuningJobCreateParams.Method.Dpo.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Dpo.Hyperparameters + .builder() + .batchSizeAuto() + .betaAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .build() + ) + .supervised( + FineTuningJobCreateParams.Method.Supervised.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Supervised + .Hyperparameters + .builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) .build() ) + .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) .build() ) - .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) + .seed(42L) + .suffix("x") + .validationFile("file-abc123") .build() ) - .seed(42L) - .suffix("x") - .validationFile("file-abc123") - .build() + } + assertThat(e.statusCode()).isEqualTo(429) + assertThat(e.error()).isEqualTo(ERROR) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + } + + @Test + fun jobsCreate500() { + val jobService = client.fineTuning().jobs() stubFor( post(anyUrl()) - .willReturn(status(500).withHeader("Foo", "Bar").withBody(toJson(OPENAI_ERROR))) + .willReturn(status(500).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON)) ) - assertThatThrownBy({ client.fineTuning().jobs().create(params) }) - .satisfies({ e -> - assertInternalServer(e, Headers.builder().put("Foo", "Bar").build(), OPENAI_ERROR) - }) - } - - @Test - fun unexpectedStatusCode() { - val params = - FineTuningJobCreateParams.builder() - .model(FineTuningJobCreateParams.Model.BABBAGE_002) - .trainingFile("file-abc123") - .hyperparameters( - FineTuningJobCreateParams.Hyperparameters.builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .addIntegration( - FineTuningJobCreateParams.Integration.builder() - .wandb( - FineTuningJobCreateParams.Integration.Wandb.builder() - .project("my-wandb-project") - .entity("entity") - .name("name") - .addTag("custom-tag") - .build() - ) - .build() - ) - .method( - FineTuningJobCreateParams.Method.builder() - .dpo( - FineTuningJobCreateParams.Method.Dpo.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Dpo.Hyperparameters.builder() - .batchSizeAuto() - .betaAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + val e = + assertThrows { + jobService.create( + FineTuningJobCreateParams.builder() + .model(FineTuningJobCreateParams.Model.BABBAGE_002) + .trainingFile("file-abc123") + .hyperparameters( + FineTuningJobCreateParams.Hyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .addIntegration( + FineTuningJobCreateParams.Integration.builder() + .wandb( + FineTuningJobCreateParams.Integration.Wandb.builder() + .project("my-wandb-project") + .entity("entity") + .name("name") + .addTag("custom-tag") .build() ) .build() ) - .supervised( - FineTuningJobCreateParams.Method.Supervised.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Supervised.Hyperparameters - .builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + .method( + FineTuningJobCreateParams.Method.builder() + .dpo( + FineTuningJobCreateParams.Method.Dpo.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Dpo.Hyperparameters + .builder() + .batchSizeAuto() + .betaAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) .build() ) + .supervised( + FineTuningJobCreateParams.Method.Supervised.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Supervised + .Hyperparameters + .builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .build() + ) + .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) .build() ) - .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) + .seed(42L) + .suffix("x") + .validationFile("file-abc123") .build() ) - .seed(42L) - .suffix("x") - .validationFile("file-abc123") - .build() + } + + assertThat(e.statusCode()).isEqualTo(500) + assertThat(e.error()).isEqualTo(ERROR) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + } + @Test + fun jobsCreate999() { + val jobService = client.fineTuning().jobs() stubFor( post(anyUrl()) - .willReturn(status(999).withHeader("Foo", "Bar").withBody(toJson(OPENAI_ERROR))) + .willReturn(status(999).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON)) ) - assertThatThrownBy({ client.fineTuning().jobs().create(params) }) - .satisfies({ e -> - assertUnexpectedStatusCodeException( - e, - 999, - Headers.builder().put("Foo", "Bar").build(), - toJson(OPENAI_ERROR), - ) - }) - } - - @Test - fun invalidBody() { - val params = - FineTuningJobCreateParams.builder() - .model(FineTuningJobCreateParams.Model.BABBAGE_002) - .trainingFile("file-abc123") - .hyperparameters( - FineTuningJobCreateParams.Hyperparameters.builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .addIntegration( - FineTuningJobCreateParams.Integration.builder() - .wandb( - FineTuningJobCreateParams.Integration.Wandb.builder() - .project("my-wandb-project") - .entity("entity") - .name("name") - .addTag("custom-tag") - .build() - ) - .build() - ) - .method( - FineTuningJobCreateParams.Method.builder() - .dpo( - FineTuningJobCreateParams.Method.Dpo.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Dpo.Hyperparameters.builder() - .batchSizeAuto() - .betaAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + val e = + assertThrows { + jobService.create( + FineTuningJobCreateParams.builder() + .model(FineTuningJobCreateParams.Model.BABBAGE_002) + .trainingFile("file-abc123") + .hyperparameters( + FineTuningJobCreateParams.Hyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .addIntegration( + FineTuningJobCreateParams.Integration.builder() + .wandb( + FineTuningJobCreateParams.Integration.Wandb.builder() + .project("my-wandb-project") + .entity("entity") + .name("name") + .addTag("custom-tag") .build() ) .build() ) - .supervised( - FineTuningJobCreateParams.Method.Supervised.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Supervised.Hyperparameters - .builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + .method( + FineTuningJobCreateParams.Method.builder() + .dpo( + FineTuningJobCreateParams.Method.Dpo.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Dpo.Hyperparameters + .builder() + .batchSizeAuto() + .betaAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .build() + ) + .supervised( + FineTuningJobCreateParams.Method.Supervised.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Supervised + .Hyperparameters + .builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) .build() ) + .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) .build() ) - .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) + .seed(42L) + .suffix("x") + .validationFile("file-abc123") .build() ) - .seed(42L) - .suffix("x") - .validationFile("file-abc123") - .build() - - stubFor(post(anyUrl()).willReturn(status(200).withBody("Not JSON"))) + } - assertThatThrownBy({ client.fineTuning().jobs().create(params) }) - .satisfies({ e -> - assertThat(e) - .isInstanceOf(OpenAIException::class.java) - .hasMessage("Error reading response") - }) + assertThat(e.statusCode()).isEqualTo(999) + assertThat(e.error()).isEqualTo(ERROR) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) } @Test - fun invalidErrorBody() { - val params = - FineTuningJobCreateParams.builder() - .model(FineTuningJobCreateParams.Model.BABBAGE_002) - .trainingFile("file-abc123") - .hyperparameters( - FineTuningJobCreateParams.Hyperparameters.builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() - .build() - ) - .addIntegration( - FineTuningJobCreateParams.Integration.builder() - .wandb( - FineTuningJobCreateParams.Integration.Wandb.builder() - .project("my-wandb-project") - .entity("entity") - .name("name") - .addTag("custom-tag") - .build() - ) - .build() - ) - .method( - FineTuningJobCreateParams.Method.builder() - .dpo( - FineTuningJobCreateParams.Method.Dpo.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Dpo.Hyperparameters.builder() - .batchSizeAuto() - .betaAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + fun jobsCreateInvalidJsonBody() { + val jobService = client.fineTuning().jobs() + stubFor( + post(anyUrl()) + .willReturn(status(200).withHeader(HEADER_NAME, HEADER_VALUE).withBody(NOT_JSON)) + ) + + val e = + assertThrows { + jobService.create( + FineTuningJobCreateParams.builder() + .model(FineTuningJobCreateParams.Model.BABBAGE_002) + .trainingFile("file-abc123") + .hyperparameters( + FineTuningJobCreateParams.Hyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .addIntegration( + FineTuningJobCreateParams.Integration.builder() + .wandb( + FineTuningJobCreateParams.Integration.Wandb.builder() + .project("my-wandb-project") + .entity("entity") + .name("name") + .addTag("custom-tag") .build() ) .build() ) - .supervised( - FineTuningJobCreateParams.Method.Supervised.builder() - .hyperparameters( - FineTuningJobCreateParams.Method.Supervised.Hyperparameters - .builder() - .batchSizeAuto() - .learningRateMultiplierAuto() - .nEpochsAuto() + .method( + FineTuningJobCreateParams.Method.builder() + .dpo( + FineTuningJobCreateParams.Method.Dpo.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Dpo.Hyperparameters + .builder() + .batchSizeAuto() + .betaAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .build() + ) + .supervised( + FineTuningJobCreateParams.Method.Supervised.builder() + .hyperparameters( + FineTuningJobCreateParams.Method.Supervised + .Hyperparameters + .builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) .build() ) + .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) .build() ) - .type(FineTuningJobCreateParams.Method.Type.SUPERVISED) + .seed(42L) + .suffix("x") + .validationFile("file-abc123") .build() ) - .seed(42L) - .suffix("x") - .validationFile("file-abc123") - .build() - - stubFor(post(anyUrl()).willReturn(status(400).withBody("Not JSON"))) - - assertThatThrownBy({ client.fineTuning().jobs().create(params) }) - .satisfies({ e -> - assertBadRequest(e, Headers.builder().build(), OpenAIError.builder().build()) - }) - } - - private fun toJson(body: T): ByteArray { - return JSON_MAPPER.writeValueAsBytes(body) - } - - private fun assertUnexpectedStatusCodeException( - throwable: Throwable, - statusCode: Int, - headers: Headers, - responseBody: ByteArray, - ) { - assertThat(throwable) - .asInstanceOf( - InstanceOfAssertFactories.throwable(UnexpectedStatusCodeException::class.java) - ) - .satisfies({ e -> - assertThat(e.statusCode()).isEqualTo(statusCode) - assertThat(e.body()).isEqualTo(String(responseBody)) - assertThat(e.headers().toMap()).containsAllEntriesOf(headers.toMap()) - }) - } - - private fun assertBadRequest(throwable: Throwable, headers: Headers, error: OpenAIError) { - assertThat(throwable) - .asInstanceOf(InstanceOfAssertFactories.throwable(BadRequestException::class.java)) - .satisfies({ e -> - assertThat(e.statusCode()).isEqualTo(400) - assertThat(e.error()).isEqualTo(error) - assertThat(e.headers().toMap()).containsAllEntriesOf(headers.toMap()) - }) - } - - private fun assertUnauthorized(throwable: Throwable, headers: Headers, error: OpenAIError) { - assertThat(throwable) - .asInstanceOf(InstanceOfAssertFactories.throwable(UnauthorizedException::class.java)) - .satisfies({ e -> - assertThat(e.statusCode()).isEqualTo(401) - assertThat(e.error()).isEqualTo(error) - assertThat(e.headers().toMap()).containsAllEntriesOf(headers.toMap()) - }) - } - - private fun assertPermissionDenied(throwable: Throwable, headers: Headers, error: OpenAIError) { - assertThat(throwable) - .asInstanceOf( - InstanceOfAssertFactories.throwable(PermissionDeniedException::class.java) - ) - .satisfies({ e -> - assertThat(e.statusCode()).isEqualTo(403) - assertThat(e.error()).isEqualTo(error) - assertThat(e.headers().toMap()).containsAllEntriesOf(headers.toMap()) - }) - } - - private fun assertNotFound(throwable: Throwable, headers: Headers, error: OpenAIError) { - assertThat(throwable) - .asInstanceOf(InstanceOfAssertFactories.throwable(NotFoundException::class.java)) - .satisfies({ e -> - assertThat(e.statusCode()).isEqualTo(404) - assertThat(e.error()).isEqualTo(error) - assertThat(e.headers().toMap()).containsAllEntriesOf(headers.toMap()) - }) - } - - private fun assertUnprocessableEntity( - throwable: Throwable, - headers: Headers, - error: OpenAIError, - ) { - assertThat(throwable) - .asInstanceOf( - InstanceOfAssertFactories.throwable(UnprocessableEntityException::class.java) - ) - .satisfies({ e -> - assertThat(e.statusCode()).isEqualTo(422) - assertThat(e.error()).isEqualTo(error) - assertThat(e.headers().toMap()).containsAllEntriesOf(headers.toMap()) - }) - } - - private fun assertRateLimit(throwable: Throwable, headers: Headers, error: OpenAIError) { - assertThat(throwable) - .asInstanceOf(InstanceOfAssertFactories.throwable(RateLimitException::class.java)) - .satisfies({ e -> - assertThat(e.statusCode()).isEqualTo(429) - assertThat(e.error()).isEqualTo(error) - assertThat(e.headers().toMap()).containsAllEntriesOf(headers.toMap()) - }) - } + } - private fun assertInternalServer(throwable: Throwable, headers: Headers, error: OpenAIError) { - assertThat(throwable) - .asInstanceOf(InstanceOfAssertFactories.throwable(InternalServerException::class.java)) - .satisfies({ e -> - assertThat(e.statusCode()).isEqualTo(500) - assertThat(e.error()).isEqualTo(error) - assertThat(e.headers().toMap()).containsAllEntriesOf(headers.toMap()) - }) + assertThat(e).hasMessage("Error reading response") } private fun Headers.toMap(): Map> = diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/FileServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/FileServiceAsyncTest.kt index 1872ceeaf..d3dc035c6 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/FileServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/FileServiceAsyncTest.kt @@ -11,7 +11,9 @@ import com.github.tomakehurst.wiremock.junit5.WireMockTest import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClientAsync import com.openai.models.FileContentParams +import com.openai.models.FileCreateParams import com.openai.models.FileDeleteParams +import com.openai.models.FilePurpose import com.openai.models.FileRetrieveParams import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -21,6 +23,27 @@ import org.junit.jupiter.api.extension.ExtendWith @WireMockTest class FileServiceAsyncTest { + @Test + fun create() { + val client = + OpenAIOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val fileServiceAsync = client.files() + + val fileObjectFuture = + fileServiceAsync.create( + FileCreateParams.builder() + .file("some content".toByteArray()) + .purpose(FilePurpose.ASSISTANTS) + .build() + ) + + val fileObject = fileObjectFuture.get() + fileObject.validate() + } + @Test fun retrieve() { val client = diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/ImageServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/ImageServiceAsyncTest.kt index 481efda86..6f5f8766e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/ImageServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/ImageServiceAsyncTest.kt @@ -4,6 +4,8 @@ package com.openai.services.async import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClientAsync +import com.openai.models.ImageCreateVariationParams +import com.openai.models.ImageEditParams import com.openai.models.ImageGenerateParams import com.openai.models.ImageModel import org.junit.jupiter.api.Test @@ -12,6 +14,58 @@ import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(TestServerExtension::class) class ImageServiceAsyncTest { + @Test + fun createVariation() { + val client = + OpenAIOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val imageServiceAsync = client.images() + + val imagesResponseFuture = + imageServiceAsync.createVariation( + ImageCreateVariationParams.builder() + .image("some content".toByteArray()) + .model(ImageModel.DALL_E_2) + .n(1L) + .responseFormat(ImageCreateVariationParams.ResponseFormat.URL) + .size(ImageCreateVariationParams.Size._256X256) + .user("user-1234") + .build() + ) + + val imagesResponse = imagesResponseFuture.get() + imagesResponse.validate() + } + + @Test + fun edit() { + val client = + OpenAIOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val imageServiceAsync = client.images() + + val imagesResponseFuture = + imageServiceAsync.edit( + ImageEditParams.builder() + .image("some content".toByteArray()) + .prompt("A cute baby sea otter wearing a beret") + .mask("some content".toByteArray()) + .model(ImageModel.DALL_E_2) + .n(1L) + .responseFormat(ImageEditParams.ResponseFormat.URL) + .size(ImageEditParams.Size._256X256) + .user("user-1234") + .build() + ) + + val imagesResponse = imagesResponseFuture.get() + imagesResponse.validate() + } + @Test fun generate() { val client = diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/audio/SpeechServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/audio/SpeechServiceAsyncTest.kt new file mode 100644 index 000000000..22b1749f7 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/audio/SpeechServiceAsyncTest.kt @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.audio + +import com.github.tomakehurst.wiremock.client.WireMock.anyUrl +import com.github.tomakehurst.wiremock.client.WireMock.ok +import com.github.tomakehurst.wiremock.client.WireMock.post +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo +import com.github.tomakehurst.wiremock.junit5.WireMockTest +import com.openai.TestServerExtension +import com.openai.client.okhttp.OpenAIOkHttpClientAsync +import com.openai.models.AudioSpeechCreateParams +import com.openai.models.SpeechModel +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +@WireMockTest +class SpeechServiceAsyncTest { + + @Test + fun create(wmRuntimeInfo: WireMockRuntimeInfo) { + val client = + OpenAIOkHttpClientAsync.builder() + .baseUrl(wmRuntimeInfo.httpBaseUrl) + .apiKey("My API Key") + .build() + val speechServiceAsync = client.audio().speech() + stubFor(post(anyUrl()).willReturn(ok().withBody("abc"))) + + val speechFuture = + speechServiceAsync.create( + AudioSpeechCreateParams.builder() + .input("input") + .model(SpeechModel.TTS_1) + .voice(AudioSpeechCreateParams.Voice.ALLOY) + .responseFormat(AudioSpeechCreateParams.ResponseFormat.MP3) + .speed(0.25) + .build() + ) + + val speech = speechFuture.get() + assertThat(speech.body()).hasContent("abc") + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/audio/TranscriptionServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/audio/TranscriptionServiceAsyncTest.kt new file mode 100644 index 000000000..d7f4e1e03 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/audio/TranscriptionServiceAsyncTest.kt @@ -0,0 +1,43 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.audio + +import com.openai.TestServerExtension +import com.openai.client.okhttp.OpenAIOkHttpClientAsync +import com.openai.models.AudioModel +import com.openai.models.AudioResponseFormat +import com.openai.models.AudioTranscriptionCreateParams +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +class TranscriptionServiceAsyncTest { + + @Test + fun create() { + val client = + OpenAIOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val transcriptionServiceAsync = client.audio().transcriptions() + + val transcriptionFuture = + transcriptionServiceAsync.create( + AudioTranscriptionCreateParams.builder() + .file("some content".toByteArray()) + .model(AudioModel.WHISPER_1) + .language("language") + .prompt("prompt") + .responseFormat(AudioResponseFormat.JSON) + .temperature(0.0) + .addTimestampGranularity( + AudioTranscriptionCreateParams.TimestampGranularity.WORD + ) + .build() + ) + + val transcription = transcriptionFuture.get() + transcription.validate() + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/audio/TranslationServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/audio/TranslationServiceAsyncTest.kt new file mode 100644 index 000000000..f0445228b --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/audio/TranslationServiceAsyncTest.kt @@ -0,0 +1,39 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.audio + +import com.openai.TestServerExtension +import com.openai.client.okhttp.OpenAIOkHttpClientAsync +import com.openai.models.AudioModel +import com.openai.models.AudioResponseFormat +import com.openai.models.AudioTranslationCreateParams +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +class TranslationServiceAsyncTest { + + @Test + fun create() { + val client = + OpenAIOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val translationServiceAsync = client.audio().translations() + + val translationFuture = + translationServiceAsync.create( + AudioTranslationCreateParams.builder() + .file("some content".toByteArray()) + .model(AudioModel.WHISPER_1) + .prompt("prompt") + .responseFormat(AudioResponseFormat.JSON) + .temperature(0.0) + .build() + ) + + val translation = translationFuture.get() + translation.validate() + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/uploads/PartServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/uploads/PartServiceAsyncTest.kt new file mode 100644 index 000000000..566cda107 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/uploads/PartServiceAsyncTest.kt @@ -0,0 +1,34 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.uploads + +import com.openai.TestServerExtension +import com.openai.client.okhttp.OpenAIOkHttpClientAsync +import com.openai.models.UploadPartCreateParams +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +class PartServiceAsyncTest { + + @Test + fun create() { + val client = + OpenAIOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val partServiceAsync = client.uploads().parts() + + val uploadPartFuture = + partServiceAsync.create( + UploadPartCreateParams.builder() + .uploadId("upload_abc123") + .data("some content".toByteArray()) + .build() + ) + + val uploadPart = uploadPartFuture.get() + uploadPart.validate() + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/FileServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/FileServiceTest.kt index 2efe09046..75abbce70 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/FileServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/FileServiceTest.kt @@ -11,7 +11,9 @@ import com.github.tomakehurst.wiremock.junit5.WireMockTest import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClient import com.openai.models.FileContentParams +import com.openai.models.FileCreateParams import com.openai.models.FileDeleteParams +import com.openai.models.FilePurpose import com.openai.models.FileRetrieveParams import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -21,6 +23,26 @@ import org.junit.jupiter.api.extension.ExtendWith @WireMockTest class FileServiceTest { + @Test + fun create() { + val client = + OpenAIOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val fileService = client.files() + + val fileObject = + fileService.create( + FileCreateParams.builder() + .file("some content".toByteArray()) + .purpose(FilePurpose.ASSISTANTS) + .build() + ) + + fileObject.validate() + } + @Test fun retrieve() { val client = diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/ImageServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/ImageServiceTest.kt index 817e2edc5..a323b41f3 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/ImageServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/ImageServiceTest.kt @@ -4,6 +4,8 @@ package com.openai.services.blocking import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClient +import com.openai.models.ImageCreateVariationParams +import com.openai.models.ImageEditParams import com.openai.models.ImageGenerateParams import com.openai.models.ImageModel import org.junit.jupiter.api.Test @@ -12,6 +14,56 @@ import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(TestServerExtension::class) class ImageServiceTest { + @Test + fun createVariation() { + val client = + OpenAIOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val imageService = client.images() + + val imagesResponse = + imageService.createVariation( + ImageCreateVariationParams.builder() + .image("some content".toByteArray()) + .model(ImageModel.DALL_E_2) + .n(1L) + .responseFormat(ImageCreateVariationParams.ResponseFormat.URL) + .size(ImageCreateVariationParams.Size._256X256) + .user("user-1234") + .build() + ) + + imagesResponse.validate() + } + + @Test + fun edit() { + val client = + OpenAIOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val imageService = client.images() + + val imagesResponse = + imageService.edit( + ImageEditParams.builder() + .image("some content".toByteArray()) + .prompt("A cute baby sea otter wearing a beret") + .mask("some content".toByteArray()) + .model(ImageModel.DALL_E_2) + .n(1L) + .responseFormat(ImageEditParams.ResponseFormat.URL) + .size(ImageEditParams.Size._256X256) + .user("user-1234") + .build() + ) + + imagesResponse.validate() + } + @Test fun generate() { val client = diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/audio/SpeechServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/audio/SpeechServiceTest.kt new file mode 100644 index 000000000..e9c5e8d56 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/audio/SpeechServiceTest.kt @@ -0,0 +1,46 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.audio + +import com.github.tomakehurst.wiremock.client.WireMock.anyUrl +import com.github.tomakehurst.wiremock.client.WireMock.ok +import com.github.tomakehurst.wiremock.client.WireMock.post +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo +import com.github.tomakehurst.wiremock.junit5.WireMockTest +import com.openai.TestServerExtension +import com.openai.client.okhttp.OpenAIOkHttpClient +import com.openai.models.AudioSpeechCreateParams +import com.openai.models.SpeechModel +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +@WireMockTest +class SpeechServiceTest { + + @Test + fun create(wmRuntimeInfo: WireMockRuntimeInfo) { + val client = + OpenAIOkHttpClient.builder() + .baseUrl(wmRuntimeInfo.httpBaseUrl) + .apiKey("My API Key") + .build() + val speechService = client.audio().speech() + stubFor(post(anyUrl()).willReturn(ok().withBody("abc"))) + + val speech = + speechService.create( + AudioSpeechCreateParams.builder() + .input("input") + .model(SpeechModel.TTS_1) + .voice(AudioSpeechCreateParams.Voice.ALLOY) + .responseFormat(AudioSpeechCreateParams.ResponseFormat.MP3) + .speed(0.25) + .build() + ) + + assertThat(speech.body()).hasContent("abc") + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/audio/TranscriptionServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/audio/TranscriptionServiceTest.kt new file mode 100644 index 000000000..6fd90c132 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/audio/TranscriptionServiceTest.kt @@ -0,0 +1,42 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.audio + +import com.openai.TestServerExtension +import com.openai.client.okhttp.OpenAIOkHttpClient +import com.openai.models.AudioModel +import com.openai.models.AudioResponseFormat +import com.openai.models.AudioTranscriptionCreateParams +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +class TranscriptionServiceTest { + + @Test + fun create() { + val client = + OpenAIOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val transcriptionService = client.audio().transcriptions() + + val transcription = + transcriptionService.create( + AudioTranscriptionCreateParams.builder() + .file("some content".toByteArray()) + .model(AudioModel.WHISPER_1) + .language("language") + .prompt("prompt") + .responseFormat(AudioResponseFormat.JSON) + .temperature(0.0) + .addTimestampGranularity( + AudioTranscriptionCreateParams.TimestampGranularity.WORD + ) + .build() + ) + + transcription.validate() + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/audio/TranslationServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/audio/TranslationServiceTest.kt new file mode 100644 index 000000000..f8716d42b --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/audio/TranslationServiceTest.kt @@ -0,0 +1,38 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.audio + +import com.openai.TestServerExtension +import com.openai.client.okhttp.OpenAIOkHttpClient +import com.openai.models.AudioModel +import com.openai.models.AudioResponseFormat +import com.openai.models.AudioTranslationCreateParams +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +class TranslationServiceTest { + + @Test + fun create() { + val client = + OpenAIOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val translationService = client.audio().translations() + + val translation = + translationService.create( + AudioTranslationCreateParams.builder() + .file("some content".toByteArray()) + .model(AudioModel.WHISPER_1) + .prompt("prompt") + .responseFormat(AudioResponseFormat.JSON) + .temperature(0.0) + .build() + ) + + translation.validate() + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/uploads/PartServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/uploads/PartServiceTest.kt new file mode 100644 index 000000000..ee1449ca8 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/uploads/PartServiceTest.kt @@ -0,0 +1,33 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.uploads + +import com.openai.TestServerExtension +import com.openai.client.okhttp.OpenAIOkHttpClient +import com.openai.models.UploadPartCreateParams +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +class PartServiceTest { + + @Test + fun create() { + val client = + OpenAIOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val partService = client.uploads().parts() + + val uploadPart = + partService.create( + UploadPartCreateParams.builder() + .uploadId("upload_abc123") + .data("some content".toByteArray()) + .build() + ) + + uploadPart.validate() + } +}