diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 59565e8e..65f558e7 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.6.1" + ".": "2.0.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 089abe5d..297d33cc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 95 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-0ee6b36cf3cc278cef4199a6aec5f7d530a6c1f17a74830037e96d50ca1edc50.yml -openapi_spec_hash: e8ec5f46bc0655b34f292422d58a60f6 -config_hash: d9b6b6e6bc85744663e300eebc482067 +configured_endpoints: 99 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-d51538ac955164de98b0c94a0a4718d96623fe39bf31a1d168be06c93c94e645.yml +openapi_spec_hash: 33e00a48df8f94c94f46290c489f132b +config_hash: c42d37618b8628ce7e1c76437db5dd8f diff --git a/CHANGELOG.md b/CHANGELOG.md index f164c88f..b882e401 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,54 @@ # Changelog +## 2.0.0 (2025-05-21) + +Full Changelog: [v1.6.1...v2.0.0](https://github.com/openai/openai-java/compare/v1.6.1...v2.0.0) + +### ⚠ BREAKING CHANGES + +* **client:** change precision of some numeric types +* **client:** extract auto pagination to shared classes +* **client:** **Migration:** - If you were referencing the `AutoPager` class on a specific `*Page` or `*PageAsync` type, then you should instead reference the shared `AutoPager` and `AutoPagerAsync` types, under the `core` package + - `AutoPagerAsync` now has different usage. You can call `.subscribe(...)` on the returned object instead to get called back each page item. You can also call `onCompleteFuture()` to get a future that completes when all items have been processed. Finally, you can call `.close()` on the returned object to stop auto-paginating early + - If you were referencing `getNextPage` or `getNextPageParams`: + - Swap to `nextPage()` and `nextPageParams()` + - Note that these both now return non-optional types (use `hasNextPage()` before calling these, since they will throw if it's impossible to get another page) + +### Features + +* **api:** Add reinforcement fine-tuning api support ([3a9fcbb](https://github.com/openai/openai-java/commit/3a9fcbb2d47a6b106d963a4243c255d55123ff9c)) +* **api:** further updates for evals API ([2b9d5bc](https://github.com/openai/openai-java/commit/2b9d5bc92d4e67cfc5b48343bd999ccbe87ca2e7)) +* **api:** manual updates ([005a643](https://github.com/openai/openai-java/commit/005a6439977311990dca20e75783657e90aa3898)) +* **api:** responses x eval api ([66327c5](https://github.com/openai/openai-java/commit/66327c540652f63147158fc9ae8ffdd5e75cbca4)) +* **api:** Updating Assistants and Evals API schemas ([02c6df6](https://github.com/openai/openai-java/commit/02c6df6457c3e03a8ae87d8b895a4838809947f3)) +* **client:** allow providing some params positionally ([1c6e875](https://github.com/openai/openai-java/commit/1c6e8759bf7e6b8a7e3b245c5a2365edf2388fca)) +* **client:** extract auto pagination to shared classes ([855d571](https://github.com/openai/openai-java/commit/855d571da1cc772b31493f6a36032934772c3757)) +* **client:** type safe structured outputs ([#463](https://github.com/openai/openai-java/issues/463)) ([e123fdd](https://github.com/openai/openai-java/commit/e123fdd3f75980b7edea84eaf6f6c101cb9a2ea2)) + + +### Bug Fixes + +* add missing `deploymentModel` params ([d9af1fb](https://github.com/openai/openai-java/commit/d9af1fbe5309ae7da0522895596573fc36162387)) +* **client:** properly support srt and vtt in audio transcriptions. ([#472](https://github.com/openai/openai-java/issues/472)) ([1e5bf3d](https://github.com/openai/openai-java/commit/1e5bf3d36cb100d1b1ad8ac328b99cc50d16e4da)) +* merge conflict ([bf7e961](https://github.com/openai/openai-java/commit/bf7e961819911f28f5ad0aaa03cda05ccfa5dbed)) +* missing validity ([301a38a](https://github.com/openai/openai-java/commit/301a38adfa5782238e407f9ea22dd9635baa4e4b)) + + +### Chores + +* **docs:** grammar improvements ([13b0fbc](https://github.com/openai/openai-java/commit/13b0fbc79ec365433ac33ccd7c5fef3e9b858fae)) +* **internal:** fix custom code ([567c86e](https://github.com/openai/openai-java/commit/567c86e958acf5898ee0be7547cfa0da34f07eb2)) + + +### Documentation + +* remove or fix invalid readme examples ([fa9f7fc](https://github.com/openai/openai-java/commit/fa9f7fc1e68b8754a50373c455cad45f59b9bda3)) + + +### Refactors + +* **client:** change precision of some numeric types ([291b0f4](https://github.com/openai/openai-java/commit/291b0f4ac12f61f31c40a80a46db2cffc9012a9a)) + ## 1.6.1 (2025-05-08) Full Changelog: [v1.6.0...v1.6.1](https://github.com/openai/openai-java/compare/v1.6.0...v1.6.1) diff --git a/README.md b/README.md index 6a05116a..f267b450 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ -[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/1.6.1) -[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/1.6.1/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/1.6.1) +[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/2.0.0) +[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/2.0.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/2.0.0) @@ -11,7 +11,7 @@ The OpenAI Java SDK provides convenient access to the [OpenAI REST API](https:// -The REST API documentation can be found on [platform.openai.com](https://platform.openai.com/docs). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.openai/openai-java/1.6.1). +The REST API documentation can be found on [platform.openai.com](https://platform.openai.com/docs). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.openai/openai-java/2.0.0). @@ -22,7 +22,7 @@ The REST API documentation can be found on [platform.openai.com](https://platfor ### Gradle ```kotlin -implementation("com.openai:openai-java:1.6.1") +implementation("com.openai:openai-java:2.0.0") ``` ### Maven @@ -31,7 +31,7 @@ implementation("com.openai:openai-java:1.6.1") com.openai openai-java - 1.6.1 + 2.0.0 ``` @@ -286,7 +286,7 @@ OpenAIClient client = OpenAIOkHttpClient.builder() The SDK provides conveniences for streamed chat completions. A [`ChatCompletionAccumulator`](openai-java-core/src/main/kotlin/com/openai/helpers/ChatCompletionAccumulator.kt) -can record the stream of chat completion chunks in the response as they are processed and accumulate +can record the stream of chat completion chunks in the response as they are processed and accumulate a [`ChatCompletion`](openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletion.kt) object similar to that which would have been returned by the non-streaming API. @@ -334,6 +334,205 @@ client.chat() ChatCompletion chatCompletion = chatCompletionAccumulator.chatCompletion(); ``` +## Structured outputs with JSON schemas + +Open AI [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs?api-mode=chat) +is a feature that ensures that the model will always generate responses that adhere to a supplied +[JSON schema](https://json-schema.org/overview/what-is-jsonschema). + +A JSON schema can be defined by creating a +[`ResponseFormatJsonSchema`](openai-java-core/src/main/kotlin/com/openai/models/ResponseFormatJsonSchema.kt) +and setting it on the input parameters. However, for greater convenience, a JSON schema can instead +be derived automatically from the structure of an arbitrary Java class. The JSON content from the +response will then be converted automatically to an instance of that Java class. A full, working +example of the use of Structured Outputs with arbitrary Java classes can be seen in +[`StructuredOutputsExample`](openai-java-example/src/main/java/com/openai/example/StructuredOutputsExample.java). + +Java classes can contain fields declared to be instances of other classes and can use collections: + +```java +class Person { + public String name; + public int birthYear; +} + +class Book { + public String title; + public Person author; + public int publicationYear; +} + +class BookList { + public List books; +} +``` + +Pass the top-level class—`BookList` in this example—to `responseFormat(Class)` when building the +parameters and then access an instance of `BookList` from the generated message content in the +response: + +```java +import com.openai.models.ChatModel; +import com.openai.models.chat.completions.ChatCompletionCreateParams; +import com.openai.models.chat.completions.StructuredChatCompletionCreateParams; + +StructuredChatCompletionCreateParams params = ChatCompletionCreateParams.builder() + .addUserMessage("List some famous late twentieth century novels.") + .model(ChatModel.GPT_4_1) + .responseFormat(BookList.class) + .build(); + +client.chat().completions().create(params).choices().stream() + .flatMap(choice -> choice.message().content().stream()) + .flatMap(bookList -> bookList.books.stream()) + .forEach(book -> System.out.println(book.title + " by " + book.author.name)); +``` + +You can start building the parameters with an instance of +[`ChatCompletionCreateParams.Builder`](openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParams.kt) +or +[`StructuredChatCompletionCreateParams.Builder`](openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletionCreateParams.kt). +If you start with the former (which allows for more compact code) the builder type will change to +the latter when `ChatCompletionCreateParams.Builder.responseFormat(Class)` is called. + +If a field in a class is optional and does not require a defined value, you can represent this using +the [`java.util.Optional`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) class. +It is up to the AI model to decide whether to provide a value for that field or leave it empty. + +```java +import java.util.Optional; + +class Book { + public String title; + public Person author; + public int publicationYear; + public Optional isbn; +} +``` + +Generic type information for fields is retained in the class's metadata, but _generic type erasure_ +applies in other scopes. While, for example, a JSON schema defining an array of books can be derived +from the `BookList.books` field with type `List`, a valid JSON schema cannot be derived from a +local variable of that same type, so the following will _not_ work: + +```java +List books = new ArrayList<>(); + +StructuredChatCompletionCreateParams> params = ChatCompletionCreateParams.builder() + .responseFormat(books.getClass()) + // ... + .build(); +``` + +If an error occurs while converting a JSON response to an instance of a Java class, the error +message will include the JSON response to assist in diagnosis. For instance, if the response is +truncated, the JSON data will be incomplete and cannot be converted to a class instance. If your +JSON response may contain sensitive information, avoid logging it directly, or ensure that you +redact any sensitive details from the error message. + +### Local JSON schema validation + +Structured Outputs supports a +[subset](https://platform.openai.com/docs/guides/structured-outputs#supported-schemas) of the JSON +Schema language. Schemas are generated automatically from classes to align with this subset. +However, due to the inherent structure of the classes, the generated schema may still violate +certain OpenAI schema restrictions, such as exceeding the maximum nesting depth or utilizing +unsupported data types. + +To facilitate compliance, the method `responseFormat(Class)` performs a validation check on the +schema derived from the specified class. This validation ensures that all restrictions are adhered +to. If any issues are detected, an exception will be thrown, providing a detailed message outlining +the reasons for the validation failure. + +- **Local Validation**: The validation process occurs locally, meaning no requests are sent to the +remote AI model. If the schema passes local validation, it is likely to pass remote validation as +well. +- **Remote Validation**: The remote AI model will conduct its own validation upon receiving the JSON +schema in the request. +- **Version Compatibility**: There may be instances where local validation fails while remote +validation succeeds. This can occur if the SDK version is outdated compared to the restrictions +enforced by the remote AI model. +- **Disabling Local Validation**: If you encounter compatibility issues and wish to bypass local +validation, you can disable it by passing +[`JsonSchemaLocalValidation.NO`](openai-java-core/src/main/kotlin/com/openai/core/JsonSchemaLocalValidation.kt) +to the `responseFormat(Class, JsonSchemaLocalValidation)` method when building the parameters. +(The default value for this parameter is `JsonSchemaLocalValidation.YES`.) + +```java +import com.openai.core.JsonSchemaLocalValidation; +import com.openai.models.ChatModel; +import com.openai.models.chat.completions.ChatCompletionCreateParams; +import com.openai.models.chat.completions.StructuredChatCompletionCreateParams; + +StructuredChatCompletionCreateParams params = ChatCompletionCreateParams.builder() + .addUserMessage("List some famous late twentieth century novels.") + .model(ChatModel.GPT_4_1) + .responseFormat(BookList.class, JsonSchemaLocalValidation.NO) + .build(); +``` + +By following these guidelines, you can ensure that your structured outputs conform to the necessary +schema requirements and minimize the risk of remote validation errors. + +### Usage with the Responses API + +_Structured Outputs_ are also supported for the Responses API. The usage is the same as described +except where the Responses API differs slightly from the Chat Completions API. Pass the top-level +class to `text(Class)` when building the parameters and then access an instance of the class from +the generated message content in the response. + +You can start building the parameters with an instance of +[`ResponseCreateParams.Builder`](openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreateParams.kt) +or +[`StructuredResponseCreateParams.Builder`](openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseCreateParams.kt). +If you start with the former (which allows for more compact code) the builder type will change to +the latter when `ResponseCreateParams.Builder.text(Class)` is called. + +For a full example of the usage of _Structured Outputs_ with the Responses API, see +[`ResponsesStructuredOutputsExample`](openai-java-example/src/main/java/com/openai/example/ResponsesStructuredOutputsExample.java). + +### Annotating classes and JSON schemas + +You can use annotations to add further information to the JSON schema derived from your Java +classes, or to exclude individual fields from the schema. Details from annotations captured in the +JSON schema may be used by the AI model to improve its response. The SDK supports the use of +[Jackson Databind](https://github.com/FasterXML/jackson-databind) annotations. + +```java +import com.fasterxml.jackson.annotation.JsonClassDescription; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; + +class Person { + @JsonPropertyDescription("The first name and surname of the person") + public String name; + public int birthYear; + @JsonPropertyDescription("The year the person died, or 'present' if the person is living.") + public String deathYear; +} + +@JsonClassDescription("The details of one published book") +class Book { + public String title; + public Person author; + @JsonPropertyDescription("The year in which the book was first published.") + public int publicationYear; + @JsonIgnore public String genre; +} + +class BookList { + public List books; +} +``` + +- Use `@JsonClassDescription` to add a detailed description to a class. +- Use `@JsonPropertyDescription` to add a detailed description to a field of a class. +- Use `@JsonIgnore` to omit a field of a class from the generated JSON schema. + +If you use `@JsonProperty(required = false)`, the `false` value will be ignored. OpenAI JSON schemas +must mark all properties as _required_, so the schema generated from your Java classes will respect +that restriction and ignore any annotation that would violate it. + ## File uploads The SDK defines methods that accept files. @@ -412,10 +611,7 @@ These methods return [`HttpResponse`](openai-java-core/src/main/kotlin/com/opena import com.openai.core.http.HttpResponse; import com.openai.models.files.FileContentParams; -FileContentParams params = FileContentParams.builder() - .fileId("file_id") - .build(); -HttpResponse response = client.files().content(params); +HttpResponse response = client.files().content("file_id"); ``` To save the response content to a file, use the [`Files.copy(...)`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#copy-java.io.InputStream-java.nio.file.Path-java.nio.file.CopyOption...-) method: @@ -528,53 +724,101 @@ The SDK throws custom unchecked exception types: ## Pagination -For methods that return a paginated list of results, this library provides convenient ways access the results either one page at a time, or item-by-item across all pages. +The SDK defines methods that return a paginated lists of results. It provides convenient ways to access the results either one page at a time or item-by-item across all pages. ### Auto-pagination -To iterate through all results across all pages, you can use `autoPager`, which automatically handles fetching more pages for you: +To iterate through all results across all pages, use the `autoPager()` method, which automatically fetches more pages as needed. -### Synchronous +When using the synchronous client, the method returns an [`Iterable`](https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html) ```java import com.openai.models.finetuning.jobs.FineTuningJob; import com.openai.models.finetuning.jobs.JobListPage; -// As an Iterable: -JobListPage page = client.fineTuning().jobs().list(params); +JobListPage page = client.fineTuning().jobs().list(); + +// Process as an Iterable for (FineTuningJob job : page.autoPager()) { System.out.println(job); -}; +} -// As a Stream: -client.fineTuning().jobs().list(params).autoPager().stream() +// Process as a Stream +page.autoPager() + .stream() .limit(50) .forEach(job -> System.out.println(job)); ``` -### Asynchronous +When using the asynchronous client, the method returns an [`AsyncStreamResponse`](openai-java-core/src/main/kotlin/com/openai/core/http/AsyncStreamResponse.kt): ```java -// Using forEach, which returns CompletableFuture: -asyncClient.fineTuning().jobs().list(params).autoPager() - .forEach(job -> System.out.println(job), executor); +import com.openai.core.http.AsyncStreamResponse; +import com.openai.models.finetuning.jobs.FineTuningJob; +import com.openai.models.finetuning.jobs.JobListPageAsync; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +CompletableFuture pageFuture = client.async().fineTuning().jobs().list(); + +pageFuture.thenRun(page -> page.autoPager().subscribe(job -> { + System.out.println(job); +})); + +// If you need to handle errors or completion of the stream +pageFuture.thenRun(page -> page.autoPager().subscribe(new AsyncStreamResponse.Handler<>() { + @Override + public void onNext(FineTuningJob job) { + System.out.println(job); + } + + @Override + public void onComplete(Optional error) { + if (error.isPresent()) { + System.out.println("Something went wrong!"); + throw new RuntimeException(error.get()); + } else { + System.out.println("No more!"); + } + } +})); + +// Or use futures +pageFuture.thenRun(page -> page.autoPager() + .subscribe(job -> { + System.out.println(job); + }) + .onCompleteFuture() + .whenComplete((unused, error) -> { + if (error != null) { + System.out.println("Something went wrong!"); + throw new RuntimeException(error); + } else { + System.out.println("No more!"); + } + })); ``` ### Manual pagination -If none of the above helpers meet your needs, you can also manually request pages one-by-one. A page of results has a `data()` method to fetch the list of objects, as well as top-level `response` and other methods to fetch top-level data about the page. It also has methods `hasNextPage`, `getNextPage`, and `getNextPageParams` methods to help with pagination. +To access individual page items and manually request the next page, use the `items()`, +`hasNextPage()`, and `nextPage()` methods: ```java import com.openai.models.finetuning.jobs.FineTuningJob; import com.openai.models.finetuning.jobs.JobListPage; -JobListPage page = client.fineTuning().jobs().list(params); -while (page != null) { - for (FineTuningJob job : page.data()) { +JobListPage page = client.fineTuning().jobs().list(); +while (true) { + for (FineTuningJob job : page.items()) { System.out.println(job); } - page = page.getNextPage().orElse(null); + if (!page.hasNextPage()) { + break; + } + + page = page.nextPage(); } ``` @@ -607,7 +851,7 @@ If the SDK threw an exception, but you're _certain_ the version is compatible, t ## Microsoft Azure -To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the same +To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the same OpenAI client builder but with the Azure-specific configuration. ```java @@ -620,7 +864,7 @@ OpenAIClient client = OpenAIOkHttpClient.builder() .build(); ``` -See the complete Azure OpenAI example in the [`openai-java-example`](openai-java-example/src/main/java/com/openai/example/AzureEntraIdExample.java) directory. The other examples in the directory also work with Azure as long as the client is configured to use it. +See the complete Azure OpenAI example in the [`openai-java-example`](openai-java-example/src/main/java/com/openai/example/AzureEntraIdExample.java) directory. The other examples in the directory also work with Azure as long as the client is configured to use it. ## Network options @@ -657,9 +901,7 @@ Requests time out after 10 minutes by default. To set a custom timeout, configure the method call using the `timeout` method: ```java -import com.openai.models.ChatModel; import com.openai.models.chat.completions.ChatCompletion; -import com.openai.models.chat.completions.ChatCompletionCreateParams; ChatCompletion chatCompletion = client.chat().completions().create( params, RequestOptions.builder().timeout(Duration.ofSeconds(30)).build() @@ -775,11 +1017,12 @@ To set a documented parameter or property to an undocumented or not yet supporte ```java import com.openai.core.JsonValue; +import com.openai.models.ChatModel; import com.openai.models.chat.completions.ChatCompletionCreateParams; ChatCompletionCreateParams params = ChatCompletionCreateParams.builder() - .addUserMessage("Say this is a test") - .model(JsonValue.from(42)) + .messages(JsonValue.from(42)) + .model(ChatModel.GPT_4_1) .build(); ``` @@ -909,9 +1152,7 @@ ChatCompletion chatCompletion = client.chat().completions().create(params).valid Or configure the method call to validate the response using the `responseValidation` method: ```java -import com.openai.models.ChatModel; import com.openai.models.chat.completions.ChatCompletion; -import com.openai.models.chat.completions.ChatCompletionCreateParams; ChatCompletion chatCompletion = client.chat().completions().create( params, RequestOptions.builder().responseValidation(true).build() diff --git a/SECURITY.md b/SECURITY.md index 3b3bd8a6..4adb0c54 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -16,13 +16,13 @@ before making any information public. ## Reporting Non-SDK Related Security Issues If you encounter security issues that are not directly related to SDKs but pertain to the services -or products provided by OpenAI please follow the respective company's security reporting guidelines. +or products provided by OpenAI, please follow the respective company's security reporting guidelines. ### OpenAI Terms and Policies Our Security Policy can be found at [Security Policy URL](https://openai.com/policies/coordinated-vulnerability-disclosure-policy). -Please contact disclosure@openai.com for any questions or concerns regarding security of our services. +Please contact disclosure@openai.com for any questions or concerns regarding the security of our services. --- diff --git a/build.gradle.kts b/build.gradle.kts index 47de6d33..581930ee 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ repositories { allprojects { group = "com.openai" - version = "1.6.1" // x-release-please-version + version = "2.0.0" // x-release-please-version } subprojects { diff --git a/openai-java-core/build.gradle.kts b/openai-java-core/build.gradle.kts index 08a91e0d..894f0e23 100644 --- a/openai-java-core/build.gradle.kts +++ b/openai-java-core/build.gradle.kts @@ -27,6 +27,8 @@ dependencies { implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.2") implementation("org.apache.httpcomponents.core5:httpcore5:5.2.4") implementation("org.apache.httpcomponents.client5:httpclient5:5.3.1") + implementation("com.github.victools:jsonschema-generator:4.38.0") + implementation("com.github.victools:jsonschema-module-jackson:4.38.0") testImplementation(kotlin("test")) testImplementation(project(":openai-java-client-okhttp")) 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 3995c9ea..e990bc66 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 @@ -11,6 +11,7 @@ import com.openai.services.blocking.EmbeddingService import com.openai.services.blocking.EvalService import com.openai.services.blocking.FileService import com.openai.services.blocking.FineTuningService +import com.openai.services.blocking.GraderService import com.openai.services.blocking.ImageService import com.openai.services.blocking.ModelService import com.openai.services.blocking.ModerationService @@ -65,6 +66,8 @@ interface OpenAIClient { fun fineTuning(): FineTuningService + fun graders(): GraderService + fun vectorStores(): VectorStoreService fun beta(): BetaService @@ -111,6 +114,8 @@ interface OpenAIClient { fun fineTuning(): FineTuningService.WithRawResponse + fun graders(): GraderService.WithRawResponse + fun vectorStores(): VectorStoreService.WithRawResponse fun beta(): BetaService.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 2a2d5894..aff5f61e 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 @@ -11,6 +11,7 @@ import com.openai.services.async.EmbeddingServiceAsync import com.openai.services.async.EvalServiceAsync import com.openai.services.async.FileServiceAsync import com.openai.services.async.FineTuningServiceAsync +import com.openai.services.async.GraderServiceAsync import com.openai.services.async.ImageServiceAsync import com.openai.services.async.ModelServiceAsync import com.openai.services.async.ModerationServiceAsync @@ -65,6 +66,8 @@ interface OpenAIClientAsync { fun fineTuning(): FineTuningServiceAsync + fun graders(): GraderServiceAsync + fun vectorStores(): VectorStoreServiceAsync fun beta(): BetaServiceAsync @@ -111,6 +114,8 @@ interface OpenAIClientAsync { fun fineTuning(): FineTuningServiceAsync.WithRawResponse + fun graders(): GraderServiceAsync.WithRawResponse + fun vectorStores(): VectorStoreServiceAsync.WithRawResponse fun beta(): BetaServiceAsync.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 2bbeb00b..5e2719a6 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 @@ -22,6 +22,8 @@ import com.openai.services.async.FileServiceAsync import com.openai.services.async.FileServiceAsyncImpl import com.openai.services.async.FineTuningServiceAsync import com.openai.services.async.FineTuningServiceAsyncImpl +import com.openai.services.async.GraderServiceAsync +import com.openai.services.async.GraderServiceAsyncImpl import com.openai.services.async.ImageServiceAsync import com.openai.services.async.ImageServiceAsyncImpl import com.openai.services.async.ModelServiceAsync @@ -84,6 +86,10 @@ class OpenAIClientAsyncImpl(private val clientOptions: ClientOptions) : OpenAICl FineTuningServiceAsyncImpl(clientOptionsWithUserAgent) } + private val graders: GraderServiceAsync by lazy { + GraderServiceAsyncImpl(clientOptionsWithUserAgent) + } + private val vectorStores: VectorStoreServiceAsync by lazy { VectorStoreServiceAsyncImpl(clientOptionsWithUserAgent) } @@ -126,6 +132,8 @@ class OpenAIClientAsyncImpl(private val clientOptions: ClientOptions) : OpenAICl override fun fineTuning(): FineTuningServiceAsync = fineTuning + override fun graders(): GraderServiceAsync = graders + override fun vectorStores(): VectorStoreServiceAsync = vectorStores override fun beta(): BetaServiceAsync = beta @@ -179,6 +187,10 @@ class OpenAIClientAsyncImpl(private val clientOptions: ClientOptions) : OpenAICl FineTuningServiceAsyncImpl.WithRawResponseImpl(clientOptions) } + private val graders: GraderServiceAsync.WithRawResponse by lazy { + GraderServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + private val vectorStores: VectorStoreServiceAsync.WithRawResponse by lazy { VectorStoreServiceAsyncImpl.WithRawResponseImpl(clientOptions) } @@ -221,6 +233,8 @@ class OpenAIClientAsyncImpl(private val clientOptions: ClientOptions) : OpenAICl override fun fineTuning(): FineTuningServiceAsync.WithRawResponse = fineTuning + override fun graders(): GraderServiceAsync.WithRawResponse = graders + override fun vectorStores(): VectorStoreServiceAsync.WithRawResponse = vectorStores override fun beta(): BetaServiceAsync.WithRawResponse = beta 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 17ad13b4..c79edfec 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 @@ -22,6 +22,8 @@ import com.openai.services.blocking.FileService import com.openai.services.blocking.FileServiceImpl import com.openai.services.blocking.FineTuningService import com.openai.services.blocking.FineTuningServiceImpl +import com.openai.services.blocking.GraderService +import com.openai.services.blocking.GraderServiceImpl import com.openai.services.blocking.ImageService import com.openai.services.blocking.ImageServiceImpl import com.openai.services.blocking.ModelService @@ -78,6 +80,8 @@ class OpenAIClientImpl(private val clientOptions: ClientOptions) : OpenAIClient FineTuningServiceImpl(clientOptionsWithUserAgent) } + private val graders: GraderService by lazy { GraderServiceImpl(clientOptionsWithUserAgent) } + private val vectorStores: VectorStoreService by lazy { VectorStoreServiceImpl(clientOptionsWithUserAgent) } @@ -116,6 +120,8 @@ class OpenAIClientImpl(private val clientOptions: ClientOptions) : OpenAIClient override fun fineTuning(): FineTuningService = fineTuning + override fun graders(): GraderService = graders + override fun vectorStores(): VectorStoreService = vectorStores override fun beta(): BetaService = beta @@ -169,6 +175,10 @@ class OpenAIClientImpl(private val clientOptions: ClientOptions) : OpenAIClient FineTuningServiceImpl.WithRawResponseImpl(clientOptions) } + private val graders: GraderService.WithRawResponse by lazy { + GraderServiceImpl.WithRawResponseImpl(clientOptions) + } + private val vectorStores: VectorStoreService.WithRawResponse by lazy { VectorStoreServiceImpl.WithRawResponseImpl(clientOptions) } @@ -211,6 +221,8 @@ class OpenAIClientImpl(private val clientOptions: ClientOptions) : OpenAIClient override fun fineTuning(): FineTuningService.WithRawResponse = fineTuning + override fun graders(): GraderService.WithRawResponse = graders + override fun vectorStores(): VectorStoreService.WithRawResponse = vectorStores override fun beta(): BetaService.WithRawResponse = beta diff --git a/openai-java-core/src/main/kotlin/com/openai/core/AutoPager.kt b/openai-java-core/src/main/kotlin/com/openai/core/AutoPager.kt new file mode 100644 index 00000000..888fd034 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/core/AutoPager.kt @@ -0,0 +1,21 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.core + +import java.util.stream.Stream +import java.util.stream.StreamSupport + +class AutoPager private constructor(private val firstPage: Page) : Iterable { + + companion object { + + fun from(firstPage: Page): AutoPager = AutoPager(firstPage) + } + + override fun iterator(): Iterator = + generateSequence(firstPage) { if (it.hasNextPage()) it.nextPage() else null } + .flatMap { it.items() } + .iterator() + + fun stream(): Stream = StreamSupport.stream(spliterator(), false) +} diff --git a/openai-java-core/src/main/kotlin/com/openai/core/AutoPagerAsync.kt b/openai-java-core/src/main/kotlin/com/openai/core/AutoPagerAsync.kt new file mode 100644 index 00000000..649be47c --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/core/AutoPagerAsync.kt @@ -0,0 +1,88 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.core + +import com.openai.core.http.AsyncStreamResponse +import java.util.Optional +import java.util.concurrent.CompletableFuture +import java.util.concurrent.CompletionException +import java.util.concurrent.Executor +import java.util.concurrent.atomic.AtomicReference + +class AutoPagerAsync +private constructor(private val firstPage: PageAsync, private val defaultExecutor: Executor) : + AsyncStreamResponse { + + companion object { + + fun from(firstPage: PageAsync, defaultExecutor: Executor): AutoPagerAsync = + AutoPagerAsync(firstPage, defaultExecutor) + } + + private val onCompleteFuture = CompletableFuture() + private val state = AtomicReference(State.NEW) + + override fun subscribe(handler: AsyncStreamResponse.Handler): AsyncStreamResponse = + subscribe(handler, defaultExecutor) + + override fun subscribe( + handler: AsyncStreamResponse.Handler, + executor: Executor, + ): AsyncStreamResponse = apply { + // TODO(JDK): Use `compareAndExchange` once targeting JDK 9. + check(state.compareAndSet(State.NEW, State.SUBSCRIBED)) { + if (state.get() == State.SUBSCRIBED) "Cannot subscribe more than once" + else "Cannot subscribe after the response is closed" + } + + fun PageAsync.handle(): CompletableFuture { + if (state.get() == State.CLOSED) { + return CompletableFuture.completedFuture(null) + } + + items().forEach { handler.onNext(it) } + return if (hasNextPage()) nextPage().thenCompose { it.handle() } + else CompletableFuture.completedFuture(null) + } + + executor.execute { + firstPage.handle().whenComplete { _, error -> + val actualError = + if (error is CompletionException && error.cause != null) error.cause else error + try { + handler.onComplete(Optional.ofNullable(actualError)) + } finally { + try { + if (actualError == null) { + onCompleteFuture.complete(null) + } else { + onCompleteFuture.completeExceptionally(actualError) + } + } finally { + close() + } + } + } + } + } + + override fun onCompleteFuture(): CompletableFuture = onCompleteFuture + + override fun close() { + val previousState = state.getAndSet(State.CLOSED) + if (previousState == State.CLOSED) { + return + } + + // When the stream is closed, we should always consider it closed. If it closed due + // to an error, then we will have already completed the future earlier, and this + // will be a no-op. + onCompleteFuture.complete(null) + } +} + +private enum class State { + NEW, + SUBSCRIBED, + CLOSED, +} 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 dc411d75..53b3b919 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,9 @@ package com.openai.core import com.fasterxml.jackson.core.Version import com.fasterxml.jackson.core.util.VersionUtil +fun checkRequired(name: String, condition: Boolean) = + check(condition) { "`$name` is required, but was not set" } + fun checkRequired(name: String, value: T?): T = checkNotNull(value) { "`$name` is required, but was not set" } diff --git a/openai-java-core/src/main/kotlin/com/openai/core/JsonSchemaLocalValidation.kt b/openai-java-core/src/main/kotlin/com/openai/core/JsonSchemaLocalValidation.kt new file mode 100644 index 00000000..9a3ae799 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/core/JsonSchemaLocalValidation.kt @@ -0,0 +1,19 @@ +package com.openai.core + +/** + * Options for local validation of JSON schemas derived from arbitrary classes before a request is + * executed. + */ +enum class JsonSchemaLocalValidation { + /** + * Validate the JSON schema locally before the request is executed. The remote AI model will + * also validate the JSON schema. + */ + YES, + + /** + * Do not validate the JSON schema locally before the request is executed. The remote AI model + * will always validate the JSON schema. + */ + NO, +} diff --git a/openai-java-core/src/main/kotlin/com/openai/core/JsonSchemaValidator.kt b/openai-java-core/src/main/kotlin/com/openai/core/JsonSchemaValidator.kt new file mode 100644 index 00000000..85c20b43 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/core/JsonSchemaValidator.kt @@ -0,0 +1,668 @@ +package com.openai.core + +import com.fasterxml.jackson.databind.JsonNode +import com.openai.core.JsonSchemaValidator.Companion.MAX_ENUM_TOTAL_STRING_LENGTH +import com.openai.core.JsonSchemaValidator.Companion.UNRESTRICTED_ENUM_VALUES_LIMIT + +/** + * A validator that ensures that a JSON schema complies with the rules and restrictions imposed by + * the OpenAI API specification for the input schemas used to define structured outputs. Only a + * subset of the JSON Schema language is supported. The purpose of this validator is to perform a + * quick check of a schema so that it can be determined to be likely to be accepted when passed in + * the request for an AI inference. + * + * This validator assumes that the JSON schema represents the structure of Java/Kotlin classes; it + * is not a general-purpose JSON schema validator. Assumptions are also made that the generator will + * be well-behaved, so the validation is not a check for strict conformance to the JSON Schema + * specification, but to the OpenAI API specification's restrictions on JSON schemas. + */ +internal class JsonSchemaValidator private constructor() { + + companion object { + // The names of the supported schema keywords. All other keywords will be rejected. + private const val SCHEMA = "\$schema" + private const val ID = "\$id" + private const val DEFS = "\$defs" + private const val REF = "\$ref" + private const val PROPS = "properties" + private const val ANY_OF = "anyOf" + private const val TYPE = "type" + private const val REQUIRED = "required" + private const val DESC = "description" + private const val TITLE = "title" + private const val ITEMS = "items" + private const val CONST = "const" + private const val ENUM = "enum" + private const val ADDITIONAL_PROPS = "additionalProperties" + + // The names of the supported schema data types. + // + // JSON Schema does not define an "integer" type, only a "number" type, but it allows any + // schema to define its own "vocabulary" of type names. "integer" is supported by OpenAI. + private const val TYPE_ARRAY = "array" + private const val TYPE_OBJECT = "object" + private const val TYPE_BOOLEAN = "boolean" + private const val TYPE_STRING = "string" + private const val TYPE_NUMBER = "number" + private const val TYPE_INTEGER = "integer" + private const val TYPE_NULL = "null" + + // The validator checks that unsupported type-specific keywords are not present in a + // property node. The OpenAI API specification states: + // + // "Notable keywords not supported include: + // + // - For strings: `minLength`, `maxLength`, `pattern`, `format` + // - For numbers: `minimum`, `maximum`, `multipleOf` + // - For objects: `patternProperties`, `unevaluatedProperties`, `propertyNames`, + // `minProperties`, `maxProperties` + // - For arrays: `unevaluatedItems`, `contains`, `minContains`, `maxContains`, `minItems`, + // `maxItems`, `uniqueItems`" + // + // As that list is not exhaustive, and no keywords are explicitly named as supported, this + // validation allows _no_ type-specific keywords. The following sets define the allowed + // keywords in different contexts and all others are rejected. + + /** + * The set of allowed keywords in the root schema only, not including the keywords that are + * also allowed in a sub-schema. + */ + private val ALLOWED_KEYWORDS_ROOT_SCHEMA_ONLY = setOf(SCHEMA, ID, DEFS) + + /** + * The set of allowed keywords when defining sub-schemas when the `"anyOf"` field is + * present. OpenAI allows the `"anyOf"` field in sub-schemas, but not in the root schema. + */ + private val ALLOWED_KEYWORDS_ANY_OF_SUB_SCHEMA = setOf(ANY_OF, TITLE, DESC) + + /** + * The set of allowed keywords when defining sub-schemas when the `"$ref"` field is present. + */ + private val ALLOWED_KEYWORDS_REF_SUB_SCHEMA = setOf(REF, TITLE, DESC) + + /** + * The set of allowed keywords when defining sub-schemas when the `"type"` field is set to + * `"object"`. + */ + private val ALLOWED_KEYWORDS_OBJECT_SUB_SCHEMA = + setOf(TYPE, REQUIRED, ADDITIONAL_PROPS, TITLE, DESC, PROPS) + + /** + * The set of allowed keywords when defining sub-schemas when the `"type"` field is set to + * `"array"`. + */ + private val ALLOWED_KEYWORDS_ARRAY_SUB_SCHEMA = setOf(TYPE, TITLE, DESC, ITEMS) + + /** + * The set of allowed keywords when defining sub-schemas when the `"type"` field is set to + * `"boolean"`, `"integer"`, `"number"`, or `"string"`. + */ + private val ALLOWED_KEYWORDS_SIMPLE_SUB_SCHEMA = setOf(TYPE, TITLE, DESC, ENUM, CONST) + + /** + * The maximum total length of all strings used in the schema for property names, definition + * names, enum values and const values. The OpenAI specification states: + * > In a schema, total string length of all property names, definition names, enum values, + * > and const values cannot exceed 15,000 characters. + */ + private const val MAX_TOTAL_STRING_LENGTH = 15_000 + + /** The maximum number of object properties allowed in a schema. */ + private const val MAX_OBJECT_PROPERTIES = 100 + + /** The maximum number of enum values across all enums in the schema. */ + private const val MAX_ENUM_VALUES = 500 + + /** + * The number of enum values in any one enum with string values beyond which a limit of + * [MAX_ENUM_TOTAL_STRING_LENGTH] is imposed on the total length of all the string values of + * that one enum. + */ + private const val UNRESTRICTED_ENUM_VALUES_LIMIT = 250 + + /** + * The maximum total length of all string values of a single enum where the number of values + * exceeds [UNRESTRICTED_ENUM_VALUES_LIMIT]. + */ + private const val MAX_ENUM_TOTAL_STRING_LENGTH = 7_500 + + /** The maximum depth (number of levels) of nesting allowed in a schema. */ + private const val MAX_NESTING_DEPTH = 5 + + /** The depth value that corresponds to the root level of the schema. */ + private const val ROOT_DEPTH = 0 + + /** + * The path string that identifies the root node in the schema when appearing in error + * messages or references. + */ + private const val ROOT_PATH = "#" + + /** + * Creates a new [JsonSchemaValidator]. After calling [validate], the validator instance + * holds information about the errors that occurred during validation (if any). A validator + * instance can be used only once to validate a schema; to validate another schema, create + * another validator. + */ + fun create() = JsonSchemaValidator() + } + + /** + * The total length of all strings used in the schema for property names, definition names, enum + * values and const values. + */ + private var totalStringLength: Int = 0 + + /** The total number of values across all enums in the schema. */ + private var totalEnumValues: Int = 0 + + /** The total number of object properties found in the schema, including in definitions. */ + private var totalObjectProperties: Int = 0 + + /** + * The set of valid references that may appear in the schema. This set includes the root schema + * and any definitions within the root schema. This is used to verify that references elsewhere + * in the schema are valid. This will always contain the root schema, but that may be the only + * member. + */ + private var validReferences: MutableSet = mutableSetOf(ROOT_PATH) + + /** The list of error messages accumulated during the validation process. */ + private val errors: MutableList = mutableListOf() + + /** + * Indicates if this validator has validated a schema or not. If a schema has been validated, + * this validator cannot be used again. + */ + private var isValidationComplete = false + + /** + * Gets the list of errors that were recorded during the validation pass. + * + * @return The list of errors. The list may be empty if no errors were recorded. In that case, + * the schema was found to be valid, or has not yet been validated by calling [validate]. + */ + fun errors(): List = errors.toImmutable() + + /** + * Indicates if a validated schema is valid or not. + * + * @return `true` if a schema has been validated by calling [validate] and no errors were + * reported; or `false` if errors were reported or if a schema has not yet been validated. + */ + fun isValid(): Boolean = isValidationComplete && errors.isEmpty() + + /** + * Validates a schema with respect to the OpenAI API specifications. + * + * @param rootSchema The root node of the tree representing the JSON schema definition. + * @return This schema validator for convenience, such as when chaining calls. + * @throws IllegalStateException If called a second time. Create a new validator to validate + * each new schema. + */ + fun validate(rootSchema: JsonNode): JsonSchemaValidator { + check(!isValidationComplete) { "Validation already complete." } + isValidationComplete = true + + validateSchema(rootSchema, ROOT_PATH, ROOT_DEPTH) + + // Verify total counts/lengths. These are not localized to a specific element in the schema, + // as no one element is the cause of the error; it is the combination of all elements that + // exceed the limits. Therefore, the root path is used in the error messages. + verify(totalEnumValues <= MAX_ENUM_VALUES, ROOT_PATH) { + "Total number of enum values ($totalEnumValues) exceeds limit of $MAX_ENUM_VALUES." + } + verify(totalStringLength <= MAX_TOTAL_STRING_LENGTH, ROOT_PATH) { + "Total string length of all values ($totalStringLength) exceeds " + + "limit of $MAX_TOTAL_STRING_LENGTH." + } + verify(totalObjectProperties <= MAX_OBJECT_PROPERTIES, ROOT_PATH) { + "Total number of object properties ($totalObjectProperties) exceeds " + + "limit of $MAX_OBJECT_PROPERTIES." + } + + return this + } + + /** + * Validates a schema. This may be the root schema or a sub-schema. Some validations are + * specific to the root schema, which is identified by the [depth] being equal to zero. + * + * This method is recursive: it will validate the given schema and any sub-schemas that it + * contains at any depth. References to other schemas (either the root schema or definition + * sub-schemas) do not increase the depth of nesting, as those references are not followed + * recursively, only checked to be valid internal schema references. + * + * @param schema The schema to be validated. This may be the root schema or any sub-schema. + * @param path The path that identifies the location of this schema within the JSON schema. For + * example, for the root schema, this will be `"#"`; for a definition sub-schema of a `Person` + * object, this will be `"#/$defs/Person"`. + * @param depth The current depth of nesting. The OpenAI API specification places a maximum + * limit on the depth of nesting, which will result in an error if it is exceeded. The nesting + * depth increases with each recursion into a nested sub-schema. For the root schema, the + * nesting depth is zero; all other sub-schemas will have a nesting depth greater than zero. + */ + private fun validateSchema(schema: JsonNode, path: String, depth: Int) { + verify(depth <= MAX_NESTING_DEPTH, path) { + "Current nesting depth is $depth, but maximum is $MAX_NESTING_DEPTH." + } + + verify(schema.isObject, path, { "Schema or sub-schema is not an object." }) { + // If the schema is not an object, perform no further validations. + return + } + + verify(!schema.isEmpty, path) { "Schema or sub-schema is empty." } + + if (depth == ROOT_DEPTH) { + // Sanity check for the presence of the "$schema" field, as this makes it more likely + // that the schema with `depth == 0` is actually the root node of a JSON schema, not + // just a generic JSON document that is being validated in error. + verify(schema.get(SCHEMA) != null, path) { "Root schema missing '$SCHEMA' field." } + } + + // Before sub-schemas can be validated, the list of definitions must be recorded to ensure + // that "$ref" references can be checked for validity. Definitions are optional and only + // appear in the root schema. + validateDefinitions(schema.get(DEFS), "$path/$DEFS", depth) + + val anyOf = schema.get(ANY_OF) + val type = schema.get(TYPE) + val ref = schema.get(REF) + + verify( + (anyOf != null).xor(type != null).xor(ref != null), + path, + { "Expected exactly one of '$TYPE' or '$ANY_OF' or '$REF'." }, + ) { + // Validation cannot continue if none are set, or if more than one is set. + return + } + + validateAnyOfSchema(schema, path, depth) + validateTypeSchema(schema, path, depth) + validateRefSchema(schema, path, depth) + } + + /** + * Validates a schema if it has an `"anyOf"` field. OpenAI does not support the use of `"anyOf"` + * at the root of a JSON schema. The value is the field is expected to be an array of valid + * sub-schemas. If the schema has no `"anyOf"` field, no action is taken. + */ + private fun validateAnyOfSchema(schema: JsonNode, path: String, depth: Int) { + val anyOf = schema.get(ANY_OF) + + if (anyOf == null) return + + validateKeywords(schema, ALLOWED_KEYWORDS_ANY_OF_SUB_SCHEMA, path, depth) + + verify( + anyOf.isArray && !anyOf.isEmpty, + path, + { "'$ANY_OF' field is not a non-empty array." }, + ) { + return + } + + // Validates that the root schema does not contain an `anyOf` field. This is a restriction + // imposed by the OpenAI API specification. `anyOf` fields _can_ appear at other depths. + verify(depth != ROOT_DEPTH, path) { "Root schema contains '$ANY_OF' field." } + + // Each entry must be a valid sub-schema. + anyOf.forEachIndexed { index, subSchema -> + validateSchema(subSchema, "$path/$ANY_OF[$index]", depth + 1) + } + } + + /** + * Validates a schema if it has a `"$ref"` field. The reference is checked to ensure it + * corresponds to a valid definition, or is a reference to the root schema. Recursive references + * are allowed. If no `"$ref"` field is found in the schema, no action is taken. + */ + private fun validateRefSchema(schema: JsonNode, path: String, depth: Int) { + val ref = schema.get(REF) + + if (ref == null) return + + validateKeywords(schema, ALLOWED_KEYWORDS_REF_SUB_SCHEMA, path, depth) + + val refPath = "$path/$REF" + + verify(ref.isTextual, refPath, { "'$REF' field is not a text value." }) { + // No point checking the reference has a referent if it is definitely malformed. + return + } + verify(ref.asText() in validReferences, refPath) { + "Invalid or unsupported reference: '${ref.asText()}'." + } + } + + /** + * Validates a schema if it has a `"type"` field. This includes most sub-schemas, except those + * that have a `"$ref"` or `"anyOf"` field instead. The `"type"` field may be set to a text + * value that is the name of the type (e.g., `"object"`, `"array"`, `"number"`), or it may be + * set to an array that contains two text values: the name of the type and `"null"`. The OpenAI + * API specification explains that this is how a property can be both required (i.e., it must + * appear in the JSON document), but its value can be optional (i.e., it can be set explicitly + * to `"null"`). If the schema has no `"type"` field, no action is taken. + */ + private fun validateTypeSchema(schema: JsonNode, path: String, depth: Int) { + val type = schema.get(TYPE) + + if (type == null) return + + val typeName = + if (type.isTextual) { + // Type will be something like `"type" : "string"` + type.asText() + } else if (type.isArray) { + // Type will be something like `"type" : [ "string", "null" ]`. This corresponds to + // the use of "Optional" in Java/Kotlin. + getTypeNameFromTypeArray(type, "$path/$TYPE") + } else { + error(path) { "'$TYPE' field is not a type name or array of type names." } + return + } + + when (typeName) { + TYPE_ARRAY -> validateArraySchema(schema, path, depth) + TYPE_OBJECT -> validateObjectSchema(schema, path, depth) + + TYPE_BOOLEAN, + TYPE_INTEGER, + TYPE_NUMBER, + TYPE_STRING -> validateSimpleSchema(schema, typeName, path, depth) + + // The type name could not be determined from a type name array. An error will already + // have been logged by `getTypeNameFromTypeArray`, so no need to do anything more here. + null -> return + + else -> error("$path/$TYPE") { "Unsupported '$TYPE' value: '$typeName'." } + } + } + + /** + * Validates a schema whose `"type"` is `"object"`. It is the responsibility of the caller to + * ensure that [schema] contains that type definition. If no type, or a different type is + * present, the behavior is not defined. + */ + private fun validateObjectSchema(schema: JsonNode, path: String, depth: Int) { + validateKeywords(schema, ALLOWED_KEYWORDS_OBJECT_SUB_SCHEMA, path, depth) + + // The schema must declare that additional properties are not allowed. For this check, it + // does not matter if there are no "properties" in the schema. + verify( + schema.get(ADDITIONAL_PROPS) != null && + schema.get(ADDITIONAL_PROPS).asBoolean() == false, + path, + ) { + "'$ADDITIONAL_PROPS' field is missing or is not set to 'false'." + } + + val properties = schema.get(PROPS) + + // The "properties" field may be missing (there may be no properties to declare), but if it + // is present, it must be a non-empty object, or validation cannot continue. + // TODO: Decide if a missing or empty "properties" field is OK or not. + verify( + properties == null || (properties.isObject && !properties.isEmpty), + path, + { "'$PROPS' field is not a non-empty object." }, + ) { + return + } + + if (properties != null) { // Must be an object. + // If a "properties" field is present, there must also be a "required" field. All + // properties must be named in the list of required properties. + validatePropertiesRequired( + properties.fieldNames().asSequence().toSet(), + schema.get(REQUIRED), + "$path/$REQUIRED", + ) + validateProperties(properties, "$path/$PROPS", depth) + } + } + + /** + * Validates a schema whose `"type"` is `"array"`. It is the responsibility of the caller to + * ensure that [schema] contains that type definition. If no type, or a different type is + * present, the behavior is not defined. + * + * An array schema must have an `"items"` field whose value is an object representing a valid + * sub-schema. + */ + private fun validateArraySchema(schema: JsonNode, path: String, depth: Int) { + validateKeywords(schema, ALLOWED_KEYWORDS_ARRAY_SUB_SCHEMA, path, depth) + + val items = schema.get(ITEMS) + + verify( + items != null && items.isObject, + path, + { "'$ITEMS' field is missing or is not an object." }, + ) { + return + } + + validateSchema(items, "$path/$ITEMS", depth + 1) + } + + /** + * Validates a schema whose `"type"` is one of the supported simple type names other than + * `"object"` and `"array"`. It is the responsibility of the caller to ensure that [schema] + * contains the correct type definition. If no type, or a different type is present, the + * behavior is not defined. + * + * @param typeName The name of the specific type of the schema. Where the field value is + * optional and the type is defined as an array of a type name and a `"null"`, this is the + * value of the non-`"null"` type name. For example `"string"`, or `"number"`. + */ + private fun validateSimpleSchema(schema: JsonNode, typeName: String, path: String, depth: Int) { + validateKeywords(schema, ALLOWED_KEYWORDS_SIMPLE_SUB_SCHEMA, path, depth) + + val enumField = schema.get(ENUM) + + // OpenAI API specification: "For a single enum property with string values, the total + // string length of all enum values cannot exceed 7,500 characters when there are more than + // 250 enum values." + val isString = typeName == TYPE_STRING + var numEnumValues = 0 + var stringLength = 0 + + enumField?.forEach { value -> + // OpenAI places limits on the total string length of all enum values across all enums + // without being specific about the type of those enums (unlike for enums with string + // values, which have their own restrictions noted above). The specification does not + // indicate how to count the string length for boolean or number values. Here it is + // assumed that their simple string representations should be counted. + val length = value.asText().length + + totalStringLength += length + totalEnumValues++ + + if (isString) { + numEnumValues++ + stringLength += length + } + } + + verify( + !isString || + numEnumValues <= UNRESTRICTED_ENUM_VALUES_LIMIT || + stringLength <= MAX_ENUM_TOTAL_STRING_LENGTH, + "$path/$ENUM", + ) { + "Total string length ($stringLength) of values of an enum with $numEnumValues " + + "values exceeds limit of $MAX_ENUM_TOTAL_STRING_LENGTH." + } + + schema.get(CONST)?.let { constValue -> totalStringLength += constValue.asText().length } + } + + /** + * Validates that the definitions (if present) contain fields that each define a valid schema. + * Records the names of any definitions to construct the set of possible valid references to + * those definitions. This set will be used to validate any references from within definition + * sub-schemas, or any other sub-schemas validated at a later time. + * + * @param defs The node containing the definitions. Definitions are optional, so this node may + * be `null`. Definitions may appear in the root schema, but will not appear in any + * sub-schemas. If no definitions are present, the list of valid references will not be + * changed and no errors will be recorded. + * @param path The path that identifies the location within the schema of the `"$defs"` node. + * @param depth The current depth of nesting. If definitions are present, this will be zero, as + * that is the depth of the root schema. + */ + private fun validateDefinitions(defs: JsonNode?, path: String, depth: Int) { + // Definitions are optional. If present, expect an object whose fields are named from the + // classes the definitions were extracted from. If not present, do not continue. + verify(defs == null || defs.isObject, path, { "'$DEFS' must be an object." }) { + return + } + + // First, record the valid references to definitions, as any definition sub-schema may + // contain a reference to any other definitions sub-schema (including itself) and those + // references need to be validated. + defs?.fieldNames()?.asSequence()?.forEach { defName -> + val reference = "$path/$defName" + + // Consider that there might be duplicate definition names if two different classes + // (from different packages) have the same simple name. That would be an error, but + // there is no need to stop the validations. + // TODO: How should duplicate names be handled? Will the generator use longer names? + verify(reference !in validReferences, path) { "Duplicate definition of '$defName'." } + validReferences += reference + } + + // Second, recursively validate the definition sub-schemas. + defs?.fieldNames()?.asSequence()?.forEach { defName -> + totalStringLength += defName.length + validateSchema(defs.get(defName), "$path/$DEFS/$defName", depth + 1) + } + } + + /** + * Validates that every property in a collection of property names appears in the array of + * property names in a `"required"` field. + * + * @param propertyNames The collection of property names to check in the array of required + * properties. This collection will not be empty. + * @param required The `"required"` field. This is expected to be a non-`null` array field with + * a set of property names. + * @param path The path identifying the location of the `"required"` field within the schema. + */ + private fun validatePropertiesRequired( + propertyNames: Collection, + required: JsonNode?, + path: String, + ) { + val requiredNames = required?.map { it.asText() }?.toSet() ?: emptySet() + + propertyNames.forEach { propertyName -> + verify(propertyName in requiredNames, path) { + "'$PROPS' field '$propertyName' is not listed as '$REQUIRED'." + } + } + } + + /** + * Validates that each named entry in the `"properties"` field of an object schema has a value + * that is a valid sub-schema. + * + * @param properties The `"properties"` field node of an object schema. + * @param path The path identifying the location of the `"properties"` field within the schema. + */ + private fun validateProperties(properties: JsonNode, path: String, depth: Int) { + val propertyNames = properties.fieldNames().asSequence().toList() + + propertyNames.forEach { propertyName -> + totalObjectProperties++ + totalStringLength += propertyName.length + validateSchema(properties.get(propertyName), "$path/$propertyName", depth + 1) + } + } + + /** + * Validates that the names of all fields in the given schema node are present in a collection + * of allowed keywords. + * + * @param depth The nesting depth of the [schema] node. If this depth is zero, an additional set + * of allowed keywords will be included automatically for keywords that are allowed to appear + * only at the root level of the schema (e.g., `"$schema"`, `"$defs"`). + */ + private fun validateKeywords( + schema: JsonNode, + allowedKeywords: Collection, + path: String, + depth: Int, + ) { + schema.fieldNames().forEach { keyword -> + verify( + keyword in allowedKeywords || + (depth == ROOT_DEPTH && keyword in ALLOWED_KEYWORDS_ROOT_SCHEMA_ONLY), + path, + ) { + "Use of '$keyword' is not supported here." + } + } + } + + /** + * Gets the name of a type from the given `"type"` field, where the field is an array that + * contains exactly two string values: a type name and a `"null"` (in any order). + * + * @param type The type node. This must be a field with an array value. If this is not an array + * field, the behavior is undefined. It is the responsibility of the caller to ensure that + * this function is only called for array fields. + * @return The type name in the array that is not the `"null"` type; or `null` if no such type + * name was found, or if the array does not contain exactly two expected values: the type name + * and a `"null"` type. If `null`, one or more validation errors will be recorded. + */ + private fun getTypeNameFromTypeArray(type: JsonNode, path: String): String? { + val types = type.asSequence().toList() + + if (types.size == 2 && types.all { it.isTextual }) { + // Allow one type name and one "null". Be lenient about the order. Assume that there are + // no oddities like type names that are empty strings, etc., as the schemas are expected + // to be generated. + if (types[1].asText() == TYPE_NULL && types[0].asText() != TYPE_NULL) { + return types[0].asText() + } else if (types[0].asText() == TYPE_NULL && types[1].asText() != TYPE_NULL) { + return types[1].asText() + } else { + error(path) { "Expected one type name and one \"$TYPE_NULL\"." } + } + } else { + error(path) { "Expected exactly two types, both strings." } + } + + return null + } + + private inline fun verify(value: Boolean, path: String, lazyMessage: () -> Any) { + verify(value, path, lazyMessage) {} + } + + private inline fun verify( + value: Boolean, + path: String, + lazyMessage: () -> Any, + onFalse: () -> Unit, + ) { + if (!value) { + error(path, lazyMessage) + onFalse() + } + } + + private inline fun error(path: String, lazyMessage: () -> Any) { + errors.add("$path: ${lazyMessage()}") + } + + override fun toString(): String = + "${javaClass.simpleName}{isValidationComplete=$isValidationComplete, " + + "totalStringLength=$totalStringLength, " + + "totalObjectProperties=$totalObjectProperties, " + + "totalEnumValues=$totalEnumValues, errors=$errors}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/core/Page.kt b/openai-java-core/src/main/kotlin/com/openai/core/Page.kt new file mode 100644 index 00000000..9c9560b4 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/core/Page.kt @@ -0,0 +1,33 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.core + +/** + * An interface representing a single page, with items of type [T], from a paginated endpoint + * response. + * + * Implementations of this interface are expected to request additional pages synchronously. For + * asynchronous pagination, see the [PageAsync] interface. + */ +interface Page { + + /** + * Returns whether there's another page after this one. + * + * The method generally doesn't make requests so the result depends entirely on the data in this + * page. If a significant amount of time has passed between requesting this page and calling + * this method, then the result could be stale. + */ + fun hasNextPage(): Boolean + + /** + * Returns the page after this one by making another request. + * + * @throws IllegalStateException if it's impossible to get the next page. This exception is + * avoidable by calling [hasNextPage] first. + */ + fun nextPage(): Page + + /** Returns the items in this page. */ + fun items(): List +} diff --git a/openai-java-core/src/main/kotlin/com/openai/core/PageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/core/PageAsync.kt new file mode 100644 index 00000000..c67ff791 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/core/PageAsync.kt @@ -0,0 +1,35 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.core + +import java.util.concurrent.CompletableFuture + +/** + * An interface representing a single page, with items of type [T], from a paginated endpoint + * response. + * + * Implementations of this interface are expected to request additional pages asynchronously. For + * synchronous pagination, see the [Page] interface. + */ +interface PageAsync { + + /** + * Returns whether there's another page after this one. + * + * The method generally doesn't make requests so the result depends entirely on the data in this + * page. If a significant amount of time has passed between requesting this page and calling + * this method, then the result could be stale. + */ + fun hasNextPage(): Boolean + + /** + * Returns the page after this one by making another request. + * + * @throws IllegalStateException if it's impossible to get the next page. This exception is + * avoidable by calling [hasNextPage] first. + */ + fun nextPage(): CompletableFuture> + + /** Returns the items in this page. */ + fun items(): List +} diff --git a/openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt b/openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt new file mode 100644 index 00000000..df48c1af --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt @@ -0,0 +1,132 @@ +package com.openai.core + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.json.JsonMapper +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.module.kotlin.kotlinModule +import com.github.victools.jsonschema.generator.Option +import com.github.victools.jsonschema.generator.OptionPreset +import com.github.victools.jsonschema.generator.SchemaGenerator +import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder +import com.github.victools.jsonschema.module.jackson.JacksonModule +import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.ResponseFormatJsonSchema +import com.openai.models.responses.ResponseFormatTextJsonSchemaConfig +import com.openai.models.responses.ResponseTextConfig + +// The SDK `ObjectMappers.jsonMapper()` requires that all fields of classes be marked with +// `@JsonProperty`, which is not desirable in this context, as it impedes usability. Therefore, a +// custom JSON mapper configuration is required. +private val MAPPER = + JsonMapper.builder() + .addModule(kotlinModule()) + .addModule(Jdk8Module()) + .addModule(JavaTimeModule()) + .build() + +/** + * Builds a response format using a JSON schema derived from the structure of an arbitrary Java + * class. + */ +@JvmSynthetic +internal fun responseFormatFromClass( + type: Class, + localValidation: JsonSchemaLocalValidation = JsonSchemaLocalValidation.YES, +): ResponseFormatJsonSchema = + ResponseFormatJsonSchema.builder() + .jsonSchema( + ResponseFormatJsonSchema.JsonSchema.builder() + .name("json-schema-from-${type.simpleName}") + .schema(JsonValue.fromJsonNode(extractAndValidateSchema(type, localValidation))) + // Ensure the model's output strictly adheres to this JSON schema. This is the + // essential "ON switch" for Structured Outputs. + .strict(true) + .build() + ) + .build() + +private fun extractAndValidateSchema( + type: Class, + localValidation: JsonSchemaLocalValidation, +): JsonNode { + val schema = extractSchema(type) + + if (localValidation == JsonSchemaLocalValidation.YES) { + val validator = JsonSchemaValidator.create().validate(schema) + + require(validator.isValid()) { + "Local validation failed for JSON schema derived from $type:\n" + + validator.errors().joinToString("\n") { " - $it" } + } + } + + return schema +} + +/** + * Builds a text configuration with its format set to a JSON schema derived from the structure of an + * arbitrary Java class. + */ +@JvmSynthetic +internal fun textConfigFromClass( + type: Class, + localValidation: JsonSchemaLocalValidation = JsonSchemaLocalValidation.YES, +): ResponseTextConfig = + ResponseTextConfig.builder() + .format( + ResponseFormatTextJsonSchemaConfig.builder() + .name("json-schema-from-${type.simpleName}") + .schema(JsonValue.fromJsonNode(extractAndValidateSchema(type, localValidation))) + // Ensure the model's output strictly adheres to this JSON schema. This is the + // essential "ON switch" for Structured Outputs. + .strict(true) + .build() + ) + .build() + +/** + * Derives a JSON schema from the structure of an arbitrary Java class. + * + * Validation is not performed by this function, as it allows extraction of the schema and + * validation of the schema to be controlled more easily when unit testing, as no exceptions will be + * thrown and any recorded validation errors can be inspected at leisure by the tests. + */ +@JvmSynthetic +internal fun extractSchema(type: Class): JsonNode { + val configBuilder = + SchemaGeneratorConfigBuilder( + com.github.victools.jsonschema.generator.SchemaVersion.DRAFT_2020_12, + OptionPreset.PLAIN_JSON, + ) + // Add `"additionalProperties" : false` to all object schemas (see OpenAI). + .with(Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT) + // Use `JacksonModule` to support the use of Jackson annotations to set property and + // class names and descriptions and to mark fields with `@JsonIgnore`. + .with(JacksonModule()) + + configBuilder + .forFields() + // For OpenAI schemas, _all_ properties _must_ be required. Override the interpretation of + // the Jackson `required` parameter to the `@JsonProperty` annotation: it will always be + // assumed to be `true`, even if explicitly `false` and even if there is no `@JsonProperty` + // annotation present. + .withRequiredCheck { true } + + return SchemaGenerator(configBuilder.build()).generateSchema(type) +} + +/** + * Creates an instance of a Java class using data from a JSON. The JSON data should conform to the + * JSON schema previously extracted from the Java class. + */ +@JvmSynthetic +internal fun responseTypeFromJson(json: String, responseType: Class): T = + try { + MAPPER.readValue(json, responseType) + } catch (e: Exception) { + // The JSON document is included in the exception message to aid diagnosis of the problem. + // It is the responsibility of the SDK user to ensure that exceptions that may contain + // sensitive data are not exposed in logs. + throw OpenAIInvalidDataException("Error parsing JSON: $json", e) + } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/ChatModel.kt b/openai-java-core/src/main/kotlin/com/openai/models/ChatModel.kt index 8546b787..e899ae2a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/ChatModel.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/ChatModel.kt @@ -86,6 +86,8 @@ class ChatModel @JsonCreator private constructor(private val value: JsonField Value.GPT_4O_SEARCH_PREVIEW_2025_03_11 GPT_4O_MINI_SEARCH_PREVIEW_2025_03_11 -> Value.GPT_4O_MINI_SEARCH_PREVIEW_2025_03_11 CHATGPT_4O_LATEST -> Value.CHATGPT_4O_LATEST + CODEX_MINI_LATEST -> Value.CODEX_MINI_LATEST GPT_4O_MINI -> Value.GPT_4O_MINI GPT_4O_MINI_2024_07_18 -> Value.GPT_4O_MINI_2024_07_18 GPT_4_TURBO -> Value.GPT_4_TURBO @@ -362,6 +367,7 @@ class ChatModel @JsonCreator private constructor(private val value: JsonField Known.GPT_4O_SEARCH_PREVIEW_2025_03_11 GPT_4O_MINI_SEARCH_PREVIEW_2025_03_11 -> Known.GPT_4O_MINI_SEARCH_PREVIEW_2025_03_11 CHATGPT_4O_LATEST -> Known.CHATGPT_4O_LATEST + CODEX_MINI_LATEST -> Known.CODEX_MINI_LATEST GPT_4O_MINI -> Known.GPT_4O_MINI GPT_4O_MINI_2024_07_18 -> Known.GPT_4O_MINI_2024_07_18 GPT_4_TURBO -> Known.GPT_4_TURBO diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParams.kt index 778b4914..226cf34a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParams.kt @@ -2,15 +2,29 @@ package com.openai.models.audio.transcriptions +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.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.Enum import com.openai.core.ExcludeMissing import com.openai.core.JsonField +import com.openai.core.JsonValue import com.openai.core.MultipartField import com.openai.core.Params +import com.openai.core.allMaxBy import com.openai.core.checkKnown import com.openai.core.checkRequired +import com.openai.core.getOrThrow import com.openai.core.http.Headers import com.openai.core.http.QueryParams import com.openai.core.toImmutable @@ -19,10 +33,12 @@ import com.openai.models.audio.AudioModel import com.openai.models.audio.AudioResponseFormat import java.io.InputStream import java.nio.file.Path +import java.util.Collections import java.util.Objects import java.util.Optional import kotlin.io.path.inputStream import kotlin.io.path.name +import kotlin.jvm.optionals.getOrNull /** Transcribes audio into the input language. */ class TranscriptionCreateParams @@ -50,6 +66,17 @@ private constructor( */ fun model(): AudioModel = body.model() + /** + * Controls how the audio is cut into chunks. When set to `"auto"`, the server first normalizes + * loudness and then uses voice activity detection (VAD) to choose boundaries. `server_vad` + * object can be provided to tweak VAD detection parameters manually. If unset, the audio is + * transcribed as a single block. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun chunkingStrategy(): Optional = body.chunkingStrategy() + /** * Additional information to include in the transcription response. `logprobs` will return the * log probabilities of the tokens in the response to understand the model's confidence in the @@ -128,6 +155,14 @@ private constructor( */ fun _model(): MultipartField = body._model() + /** + * Returns the raw multipart value of [chunkingStrategy]. + * + * Unlike [chunkingStrategy], this method doesn't throw if the multipart field has an unexpected + * type. + */ + fun _chunkingStrategy(): MultipartField = body._chunkingStrategy() + /** * Returns the raw multipart value of [include]. * @@ -215,9 +250,9 @@ private constructor( * Otherwise, it's more convenient to use the top-level setters instead: * - [file] * - [model] + * - [chunkingStrategy] * - [include] * - [language] - * - [prompt] * - etc. */ fun body(body: Body) = apply { this.body = body.toBuilder() } @@ -272,6 +307,39 @@ private constructor( */ fun model(value: String) = apply { body.model(value) } + /** + * Controls how the audio is cut into chunks. When set to `"auto"`, the server first + * normalizes loudness and then uses voice activity detection (VAD) to choose boundaries. + * `server_vad` object can be provided to tweak VAD detection parameters manually. If unset, + * the audio is transcribed as a single block. + */ + fun chunkingStrategy(chunkingStrategy: ChunkingStrategy?) = apply { + body.chunkingStrategy(chunkingStrategy) + } + + /** Alias for calling [Builder.chunkingStrategy] with `chunkingStrategy.orElse(null)`. */ + fun chunkingStrategy(chunkingStrategy: Optional) = + chunkingStrategy(chunkingStrategy.getOrNull()) + + /** + * Sets [Builder.chunkingStrategy] to an arbitrary multipart value. + * + * You should usually call [Builder.chunkingStrategy] with a well-typed [ChunkingStrategy] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun chunkingStrategy(chunkingStrategy: MultipartField) = apply { + body.chunkingStrategy(chunkingStrategy) + } + + /** Alias for calling [chunkingStrategy] with `ChunkingStrategy.ofAuto()`. */ + fun chunkingStrategyAuto() = apply { body.chunkingStrategyAuto() } + + /** Alias for calling [chunkingStrategy] with `ChunkingStrategy.ofVadConfig(vadConfig)`. */ + fun chunkingStrategy(vadConfig: ChunkingStrategy.VadConfig) = apply { + body.chunkingStrategy(vadConfig) + } + /** * Additional information to include in the transcription response. `logprobs` will return * the log probabilities of the tokens in the response to understand the model's confidence @@ -521,6 +589,7 @@ private constructor( mapOf( "file" to _file(), "model" to _model(), + "chunking_strategy" to _chunkingStrategy(), "include" to _include(), "language" to _language(), "prompt" to _prompt(), @@ -538,6 +607,7 @@ private constructor( private constructor( private val file: MultipartField, private val model: MultipartField, + private val chunkingStrategy: MultipartField, private val include: MultipartField>, private val language: MultipartField, private val prompt: MultipartField, @@ -564,6 +634,18 @@ private constructor( */ fun model(): AudioModel = model.value.getRequired("model") + /** + * Controls how the audio is cut into chunks. When set to `"auto"`, the server first + * normalizes loudness and then uses voice activity detection (VAD) to choose boundaries. + * `server_vad` object can be provided to tweak VAD detection parameters manually. If unset, + * the audio is transcribed as a single block. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun chunkingStrategy(): Optional = + chunkingStrategy.value.getOptional("chunking_strategy") + /** * Additional information to include in the transcription response. `logprobs` will return * the log probabilities of the tokens in the response to understand the model's confidence @@ -644,6 +726,16 @@ private constructor( */ @JsonProperty("model") @ExcludeMissing fun _model(): MultipartField = model + /** + * Returns the raw multipart value of [chunkingStrategy]. + * + * Unlike [chunkingStrategy], this method doesn't throw if the multipart field has an + * unexpected type. + */ + @JsonProperty("chunking_strategy") + @ExcludeMissing + fun _chunkingStrategy(): MultipartField = chunkingStrategy + /** * Returns the raw multipart value of [include]. * @@ -721,6 +813,7 @@ private constructor( private var file: MultipartField? = null private var model: MultipartField? = null + private var chunkingStrategy: MultipartField = MultipartField.of(null) private var include: MultipartField>? = null private var language: MultipartField = MultipartField.of(null) private var prompt: MultipartField = MultipartField.of(null) @@ -734,6 +827,7 @@ private constructor( internal fun from(body: Body) = apply { file = body.file model = body.model + chunkingStrategy = body.chunkingStrategy include = body.include.map { it.toMutableList() } language = body.language prompt = body.prompt @@ -800,6 +894,41 @@ private constructor( */ fun model(value: String) = model(AudioModel.of(value)) + /** + * Controls how the audio is cut into chunks. When set to `"auto"`, the server first + * normalizes loudness and then uses voice activity detection (VAD) to choose + * boundaries. `server_vad` object can be provided to tweak VAD detection parameters + * manually. If unset, the audio is transcribed as a single block. + */ + fun chunkingStrategy(chunkingStrategy: ChunkingStrategy?) = + chunkingStrategy(MultipartField.of(chunkingStrategy)) + + /** + * Alias for calling [Builder.chunkingStrategy] with `chunkingStrategy.orElse(null)`. + */ + fun chunkingStrategy(chunkingStrategy: Optional) = + chunkingStrategy(chunkingStrategy.getOrNull()) + + /** + * Sets [Builder.chunkingStrategy] to an arbitrary multipart value. + * + * You should usually call [Builder.chunkingStrategy] with a well-typed + * [ChunkingStrategy] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun chunkingStrategy(chunkingStrategy: MultipartField) = apply { + this.chunkingStrategy = chunkingStrategy + } + + /** Alias for calling [chunkingStrategy] with `ChunkingStrategy.ofAuto()`. */ + fun chunkingStrategyAuto() = chunkingStrategy(ChunkingStrategy.ofAuto()) + + /** + * Alias for calling [chunkingStrategy] with `ChunkingStrategy.ofVadConfig(vadConfig)`. + */ + fun chunkingStrategy(vadConfig: ChunkingStrategy.VadConfig) = + chunkingStrategy(ChunkingStrategy.ofVadConfig(vadConfig)) + /** * Additional information to include in the transcription response. `logprobs` will * return the log probabilities of the tokens in the response to understand the model's @@ -953,6 +1082,7 @@ private constructor( Body( checkRequired("file", file), checkRequired("model", model), + chunkingStrategy, (include ?: MultipartField.of(null)).map { it.toImmutable() }, language, prompt, @@ -971,6 +1101,7 @@ private constructor( file() model().validate() + chunkingStrategy().ifPresent { it.validate() } include().ifPresent { it.forEach { it.validate() } } language() prompt() @@ -993,17 +1124,612 @@ private constructor( return true } - return /* spotless:off */ other is Body && file == other.file && model == other.model && include == other.include && language == other.language && prompt == other.prompt && responseFormat == other.responseFormat && temperature == other.temperature && timestampGranularities == other.timestampGranularities /* spotless:on */ + return /* spotless:off */ other is Body && file == other.file && model == other.model && chunkingStrategy == other.chunkingStrategy && include == other.include && 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, include, language, prompt, responseFormat, temperature, timestampGranularities) } + private val hashCode: Int by lazy { Objects.hash(file, model, chunkingStrategy, include, language, prompt, responseFormat, temperature, timestampGranularities) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "Body{file=$file, model=$model, include=$include, language=$language, prompt=$prompt, responseFormat=$responseFormat, temperature=$temperature, timestampGranularities=$timestampGranularities}" + "Body{file=$file, model=$model, chunkingStrategy=$chunkingStrategy, include=$include, language=$language, prompt=$prompt, responseFormat=$responseFormat, temperature=$temperature, timestampGranularities=$timestampGranularities}" + } + + /** + * Controls how the audio is cut into chunks. When set to `"auto"`, the server first normalizes + * loudness and then uses voice activity detection (VAD) to choose boundaries. `server_vad` + * object can be provided to tweak VAD detection parameters manually. If unset, the audio is + * transcribed as a single block. + */ + @JsonDeserialize(using = ChunkingStrategy.Deserializer::class) + @JsonSerialize(using = ChunkingStrategy.Serializer::class) + class ChunkingStrategy + private constructor( + private val auto: JsonValue? = null, + private val vadConfig: VadConfig? = null, + private val _json: JsonValue? = null, + ) { + + /** Automatically set chunking parameters based on the audio. Must be set to `"auto"`. */ + fun auto(): Optional = Optional.ofNullable(auto) + + fun vadConfig(): Optional = Optional.ofNullable(vadConfig) + + fun isAuto(): Boolean = auto != null + + fun isVadConfig(): Boolean = vadConfig != null + + /** Automatically set chunking parameters based on the audio. Must be set to `"auto"`. */ + fun asAuto(): JsonValue = auto.getOrThrow("auto") + + fun asVadConfig(): VadConfig = vadConfig.getOrThrow("vadConfig") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + auto != null -> visitor.visitAuto(auto) + vadConfig != null -> visitor.visitVadConfig(vadConfig) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): ChunkingStrategy = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) { + auto.let { + if (it != JsonValue.from("auto")) { + throw OpenAIInvalidDataException("'auto' is invalid, received $it") + } + } + } + + override fun visitVadConfig(vadConfig: VadConfig) { + vadConfig.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitVadConfig(vadConfig: VadConfig) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is ChunkingStrategy && auto == other.auto && vadConfig == other.vadConfig /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, vadConfig) /* spotless:on */ + + override fun toString(): String = + when { + auto != null -> "ChunkingStrategy{auto=$auto}" + vadConfig != null -> "ChunkingStrategy{vadConfig=$vadConfig}" + _json != null -> "ChunkingStrategy{_unknown=$_json}" + else -> throw IllegalStateException("Invalid ChunkingStrategy") + } + + companion object { + + /** + * Automatically set chunking parameters based on the audio. Must be set to `"auto"`. + */ + @JvmStatic fun ofAuto() = ChunkingStrategy(auto = JsonValue.from("auto")) + + @JvmStatic + fun ofVadConfig(vadConfig: VadConfig) = ChunkingStrategy(vadConfig = vadConfig) + } + + /** + * An interface that defines how to map each variant of [ChunkingStrategy] to a value of + * type [T]. + */ + interface Visitor { + + /** + * Automatically set chunking parameters based on the audio. Must be set to `"auto"`. + */ + fun visitAuto(auto: JsonValue): T + + fun visitVadConfig(vadConfig: VadConfig): T + + /** + * Maps an unknown variant of [ChunkingStrategy] to a value of type [T]. + * + * An instance of [ChunkingStrategy] 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 ChunkingStrategy: $json") + } + } + + internal class Deserializer : BaseDeserializer(ChunkingStrategy::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): ChunkingStrategy { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { ChunkingStrategy(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + ChunkingStrategy(vadConfig = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from array). + 0 -> ChunkingStrategy(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(ChunkingStrategy::class) { + + override fun serialize( + value: ChunkingStrategy, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.auto != null -> generator.writeObject(value.auto) + value.vadConfig != null -> generator.writeObject(value.vadConfig) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid ChunkingStrategy") + } + } + } + + class VadConfig + private constructor( + private val type: MultipartField, + private val prefixPaddingMs: MultipartField, + private val silenceDurationMs: MultipartField, + private val threshold: MultipartField, + private val additionalProperties: MutableMap, + ) { + + /** + * Must be set to `server_vad` to enable manual chunking using server side VAD. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun type(): Type = type.value.getRequired("type") + + /** + * Amount of audio to include before the VAD detected speech (in milliseconds). + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun prefixPaddingMs(): Optional = + prefixPaddingMs.value.getOptional("prefix_padding_ms") + + /** + * Duration of silence to detect speech stop (in milliseconds). With shorter values the + * model will respond more quickly, but may jump in on short pauses from the user. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun silenceDurationMs(): Optional = + silenceDurationMs.value.getOptional("silence_duration_ms") + + /** + * Sensitivity threshold (0.0 to 1.0) for voice activity detection. A higher threshold + * will require louder audio to activate the model, and thus might perform better in + * noisy environments. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun threshold(): Optional = threshold.value.getOptional("threshold") + + /** + * Returns the raw multipart value of [type]. + * + * Unlike [type], this method doesn't throw if the multipart field has an unexpected + * type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): MultipartField = type + + /** + * Returns the raw multipart value of [prefixPaddingMs]. + * + * Unlike [prefixPaddingMs], this method doesn't throw if the multipart field has an + * unexpected type. + */ + @JsonProperty("prefix_padding_ms") + @ExcludeMissing + fun _prefixPaddingMs(): MultipartField = prefixPaddingMs + + /** + * Returns the raw multipart value of [silenceDurationMs]. + * + * Unlike [silenceDurationMs], this method doesn't throw if the multipart field has an + * unexpected type. + */ + @JsonProperty("silence_duration_ms") + @ExcludeMissing + fun _silenceDurationMs(): MultipartField = silenceDurationMs + + /** + * Returns the raw multipart value of [threshold]. + * + * Unlike [threshold], this method doesn't throw if the multipart field has an + * unexpected type. + */ + @JsonProperty("threshold") + @ExcludeMissing + fun _threshold(): MultipartField = threshold + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [VadConfig]. + * + * The following fields are required: + * ```java + * .type() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [VadConfig]. */ + class Builder internal constructor() { + + private var type: MultipartField? = null + private var prefixPaddingMs: MultipartField = MultipartField.of(null) + private var silenceDurationMs: MultipartField = MultipartField.of(null) + private var threshold: MultipartField = MultipartField.of(null) + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(vadConfig: VadConfig) = apply { + type = vadConfig.type + prefixPaddingMs = vadConfig.prefixPaddingMs + silenceDurationMs = vadConfig.silenceDurationMs + threshold = vadConfig.threshold + additionalProperties = vadConfig.additionalProperties.toMutableMap() + } + + /** Must be set to `server_vad` to enable manual chunking using server side VAD. */ + fun type(type: Type) = type(MultipartField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary multipart value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: MultipartField) = apply { this.type = type } + + /** Amount of audio to include before the VAD detected speech (in milliseconds). */ + fun prefixPaddingMs(prefixPaddingMs: Long) = + prefixPaddingMs(MultipartField.of(prefixPaddingMs)) + + /** + * Sets [Builder.prefixPaddingMs] to an arbitrary multipart value. + * + * You should usually call [Builder.prefixPaddingMs] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun prefixPaddingMs(prefixPaddingMs: MultipartField) = apply { + this.prefixPaddingMs = prefixPaddingMs + } + + /** + * Duration of silence to detect speech stop (in milliseconds). With shorter values + * the model will respond more quickly, but may jump in on short pauses from the + * user. + */ + fun silenceDurationMs(silenceDurationMs: Long) = + silenceDurationMs(MultipartField.of(silenceDurationMs)) + + /** + * Sets [Builder.silenceDurationMs] to an arbitrary multipart value. + * + * You should usually call [Builder.silenceDurationMs] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun silenceDurationMs(silenceDurationMs: MultipartField) = apply { + this.silenceDurationMs = silenceDurationMs + } + + /** + * Sensitivity threshold (0.0 to 1.0) for voice activity detection. A higher + * threshold will require louder audio to activate the model, and thus might perform + * better in noisy environments. + */ + fun threshold(threshold: Double) = threshold(MultipartField.of(threshold)) + + /** + * Sets [Builder.threshold] to an arbitrary multipart value. + * + * You should usually call [Builder.threshold] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun threshold(threshold: MultipartField) = apply { + this.threshold = threshold + } + + 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) + } + + /** + * Returns an immutable instance of [VadConfig]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .type() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): VadConfig = + VadConfig( + checkRequired("type", type), + prefixPaddingMs, + silenceDurationMs, + threshold, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): VadConfig = apply { + if (validated) { + return@apply + } + + type().validate() + prefixPaddingMs() + silenceDurationMs() + threshold() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** Must be set to `server_vad` to enable manual chunking using server side VAD. */ + class Type @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 SERVER_VAD = of("server_vad") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + SERVER_VAD + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] 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 { + SERVER_VAD, + /** + * An enum member indicating that [Type] 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) { + SERVER_VAD -> Value.SERVER_VAD + 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) { + SERVER_VAD -> Known.SERVER_VAD + else -> throw OpenAIInvalidDataException("Unknown Type: $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") + } + + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Type && 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 VadConfig && type == other.type && prefixPaddingMs == other.prefixPaddingMs && silenceDurationMs == other.silenceDurationMs && threshold == other.threshold && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(type, prefixPaddingMs, silenceDurationMs, threshold, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "VadConfig{type=$type, prefixPaddingMs=$prefixPaddingMs, silenceDurationMs=$silenceDurationMs, threshold=$threshold, additionalProperties=$additionalProperties}" + } } class TimestampGranularity diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegment.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegment.kt index ffd5f56d..c2de7f04 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegment.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegment.kt @@ -21,13 +21,13 @@ import kotlin.jvm.optionals.getOrNull class TranscriptionSegment private constructor( private val id: JsonField, - private val avgLogprob: JsonField, - private val compressionRatio: JsonField, - private val end: JsonField, - private val noSpeechProb: JsonField, + private val avgLogprob: JsonField, + private val compressionRatio: JsonField, + private val end: JsonField, + private val noSpeechProb: JsonField, private val seek: JsonField, - private val start: JsonField, - private val temperature: JsonField, + private val start: JsonField, + private val temperature: JsonField, private val text: JsonField, private val tokens: JsonField>, private val additionalProperties: MutableMap, @@ -38,19 +38,19 @@ private constructor( @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), @JsonProperty("avg_logprob") @ExcludeMissing - avgLogprob: JsonField = JsonMissing.of(), + avgLogprob: JsonField = JsonMissing.of(), @JsonProperty("compression_ratio") @ExcludeMissing - compressionRatio: JsonField = JsonMissing.of(), - @JsonProperty("end") @ExcludeMissing end: JsonField = JsonMissing.of(), + compressionRatio: JsonField = JsonMissing.of(), + @JsonProperty("end") @ExcludeMissing end: JsonField = JsonMissing.of(), @JsonProperty("no_speech_prob") @ExcludeMissing - noSpeechProb: JsonField = JsonMissing.of(), + noSpeechProb: JsonField = JsonMissing.of(), @JsonProperty("seek") @ExcludeMissing seek: JsonField = JsonMissing.of(), - @JsonProperty("start") @ExcludeMissing start: JsonField = JsonMissing.of(), + @JsonProperty("start") @ExcludeMissing start: JsonField = JsonMissing.of(), @JsonProperty("temperature") @ExcludeMissing - temperature: JsonField = JsonMissing.of(), + temperature: JsonField = JsonMissing.of(), @JsonProperty("text") @ExcludeMissing text: JsonField = JsonMissing.of(), @JsonProperty("tokens") @ExcludeMissing tokens: JsonField> = JsonMissing.of(), ) : this( @@ -81,7 +81,7 @@ private constructor( * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - fun avgLogprob(): Double = avgLogprob.getRequired("avg_logprob") + fun avgLogprob(): Float = avgLogprob.getRequired("avg_logprob") /** * Compression ratio of the segment. If the value is greater than 2.4, consider the compression @@ -90,7 +90,7 @@ private constructor( * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - fun compressionRatio(): Double = compressionRatio.getRequired("compression_ratio") + fun compressionRatio(): Float = compressionRatio.getRequired("compression_ratio") /** * End time of the segment in seconds. @@ -98,7 +98,7 @@ private constructor( * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - fun end(): Double = end.getRequired("end") + fun end(): Float = end.getRequired("end") /** * Probability of no speech in the segment. If the value is higher than 1.0 and the @@ -107,7 +107,7 @@ private constructor( * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - fun noSpeechProb(): Double = noSpeechProb.getRequired("no_speech_prob") + fun noSpeechProb(): Float = noSpeechProb.getRequired("no_speech_prob") /** * Seek offset of the segment. @@ -123,7 +123,7 @@ private constructor( * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - fun start(): Double = start.getRequired("start") + fun start(): Float = start.getRequired("start") /** * Temperature parameter used for generating the segment. @@ -131,7 +131,7 @@ private constructor( * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - fun temperature(): Double = temperature.getRequired("temperature") + fun temperature(): Float = temperature.getRequired("temperature") /** * Text content of the segment. @@ -161,7 +161,7 @@ private constructor( * * Unlike [avgLogprob], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("avg_logprob") @ExcludeMissing fun _avgLogprob(): JsonField = avgLogprob + @JsonProperty("avg_logprob") @ExcludeMissing fun _avgLogprob(): JsonField = avgLogprob /** * Returns the raw JSON value of [compressionRatio]. @@ -171,14 +171,14 @@ private constructor( */ @JsonProperty("compression_ratio") @ExcludeMissing - fun _compressionRatio(): JsonField = compressionRatio + fun _compressionRatio(): JsonField = compressionRatio /** * Returns the raw JSON value of [end]. * * Unlike [end], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("end") @ExcludeMissing fun _end(): JsonField = end + @JsonProperty("end") @ExcludeMissing fun _end(): JsonField = end /** * Returns the raw JSON value of [noSpeechProb]. @@ -187,7 +187,7 @@ private constructor( */ @JsonProperty("no_speech_prob") @ExcludeMissing - fun _noSpeechProb(): JsonField = noSpeechProb + fun _noSpeechProb(): JsonField = noSpeechProb /** * Returns the raw JSON value of [seek]. @@ -201,14 +201,14 @@ private constructor( * * Unlike [start], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("start") @ExcludeMissing fun _start(): JsonField = start + @JsonProperty("start") @ExcludeMissing fun _start(): JsonField = start /** * Returns the raw JSON value of [temperature]. * * Unlike [temperature], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("temperature") @ExcludeMissing fun _temperature(): JsonField = temperature + @JsonProperty("temperature") @ExcludeMissing fun _temperature(): JsonField = temperature /** * Returns the raw JSON value of [text]. @@ -262,13 +262,13 @@ private constructor( 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 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 start: JsonField? = null + private var temperature: JsonField? = null private var text: JsonField? = null private var tokens: JsonField>? = null private var additionalProperties: MutableMap = mutableMapOf() @@ -303,60 +303,60 @@ private constructor( * Average logprob of the segment. If the value is lower than -1, consider the logprobs * failed. */ - fun avgLogprob(avgLogprob: Double) = avgLogprob(JsonField.of(avgLogprob)) + fun avgLogprob(avgLogprob: Float) = avgLogprob(JsonField.of(avgLogprob)) /** * Sets [Builder.avgLogprob] to an arbitrary JSON value. * - * You should usually call [Builder.avgLogprob] with a well-typed [Double] value instead. + * You should usually call [Builder.avgLogprob] with a well-typed [Float] value instead. * This method is primarily for setting the field to an undocumented or not yet supported * value. */ - fun avgLogprob(avgLogprob: JsonField) = apply { this.avgLogprob = avgLogprob } + 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) = + fun compressionRatio(compressionRatio: Float) = compressionRatio(JsonField.of(compressionRatio)) /** * Sets [Builder.compressionRatio] to an arbitrary JSON value. * - * You should usually call [Builder.compressionRatio] with a well-typed [Double] value + * You should usually call [Builder.compressionRatio] with a well-typed [Float] value * instead. This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun compressionRatio(compressionRatio: JsonField) = apply { + fun compressionRatio(compressionRatio: JsonField) = apply { this.compressionRatio = compressionRatio } /** End time of the segment in seconds. */ - fun end(end: Double) = end(JsonField.of(end)) + fun end(end: Float) = end(JsonField.of(end)) /** * Sets [Builder.end] to an arbitrary JSON value. * - * You should usually call [Builder.end] with a well-typed [Double] value instead. This + * You should usually call [Builder.end] with a well-typed [Float] value instead. This * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun end(end: JsonField) = apply { this.end = end } + 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)) + fun noSpeechProb(noSpeechProb: Float) = noSpeechProb(JsonField.of(noSpeechProb)) /** * Sets [Builder.noSpeechProb] to an arbitrary JSON value. * - * You should usually call [Builder.noSpeechProb] with a well-typed [Double] value instead. + * You should usually call [Builder.noSpeechProb] with a well-typed [Float] value instead. * This method is primarily for setting the field to an undocumented or not yet supported * value. */ - fun noSpeechProb(noSpeechProb: JsonField) = apply { + fun noSpeechProb(noSpeechProb: JsonField) = apply { this.noSpeechProb = noSpeechProb } @@ -372,27 +372,27 @@ private constructor( fun seek(seek: JsonField) = apply { this.seek = seek } /** Start time of the segment in seconds. */ - fun start(start: Double) = start(JsonField.of(start)) + fun start(start: Float) = start(JsonField.of(start)) /** * Sets [Builder.start] to an arbitrary JSON value. * - * You should usually call [Builder.start] with a well-typed [Double] value instead. This + * You should usually call [Builder.start] with a well-typed [Float] value instead. This * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun start(start: JsonField) = apply { this.start = start } + fun start(start: JsonField) = apply { this.start = start } /** Temperature parameter used for generating the segment. */ - fun temperature(temperature: Double) = temperature(JsonField.of(temperature)) + fun temperature(temperature: Float) = temperature(JsonField.of(temperature)) /** * Sets [Builder.temperature] to an arbitrary JSON value. * - * You should usually call [Builder.temperature] with a well-typed [Double] value instead. + * You should usually call [Builder.temperature] with a well-typed [Float] value instead. * This method is primarily for setting the field to an undocumented or not yet supported * value. */ - fun temperature(temperature: JsonField) = apply { this.temperature = temperature } + fun temperature(temperature: JsonField) = apply { this.temperature = temperature } /** Text content of the segment. */ fun text(text: String) = text(JsonField.of(text)) diff --git a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionWord.kt b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionWord.kt index e17f2505..2ebd3c15 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionWord.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/audio/transcriptions/TranscriptionWord.kt @@ -17,16 +17,16 @@ import java.util.Objects class TranscriptionWord private constructor( - private val end: JsonField, - private val start: JsonField, + private val end: JsonField, + private val start: JsonField, private val word: JsonField, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("end") @ExcludeMissing end: JsonField = JsonMissing.of(), - @JsonProperty("start") @ExcludeMissing start: JsonField = JsonMissing.of(), + @JsonProperty("end") @ExcludeMissing end: JsonField = JsonMissing.of(), + @JsonProperty("start") @ExcludeMissing start: JsonField = JsonMissing.of(), @JsonProperty("word") @ExcludeMissing word: JsonField = JsonMissing.of(), ) : this(end, start, word, mutableMapOf()) @@ -36,7 +36,7 @@ private constructor( * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - fun end(): Double = end.getRequired("end") + fun end(): Float = end.getRequired("end") /** * Start time of the word in seconds. @@ -44,7 +44,7 @@ private constructor( * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - fun start(): Double = start.getRequired("start") + fun start(): Float = start.getRequired("start") /** * The text content of the word. @@ -59,14 +59,14 @@ private constructor( * * Unlike [end], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("end") @ExcludeMissing fun _end(): JsonField = end + @JsonProperty("end") @ExcludeMissing fun _end(): JsonField = end /** * Returns the raw JSON value of [start]. * * Unlike [start], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("start") @ExcludeMissing fun _start(): JsonField = start + @JsonProperty("start") @ExcludeMissing fun _start(): JsonField = start /** * Returns the raw JSON value of [word]. @@ -105,8 +105,8 @@ private constructor( /** A builder for [TranscriptionWord]. */ class Builder internal constructor() { - private var end: JsonField? = null - private var start: JsonField? = null + private var end: JsonField? = null + private var start: JsonField? = null private var word: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() @@ -119,26 +119,26 @@ private constructor( } /** End time of the word in seconds. */ - fun end(end: Double) = end(JsonField.of(end)) + fun end(end: Float) = end(JsonField.of(end)) /** * Sets [Builder.end] to an arbitrary JSON value. * - * You should usually call [Builder.end] with a well-typed [Double] value instead. This + * You should usually call [Builder.end] with a well-typed [Float] value instead. This * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun end(end: JsonField) = apply { this.end = end } + fun end(end: JsonField) = apply { this.end = end } /** Start time of the word in seconds. */ - fun start(start: Double) = start(JsonField.of(start)) + fun start(start: Float) = start(JsonField.of(start)) /** * Sets [Builder.start] to an arbitrary JSON value. * - * You should usually call [Builder.start] with a well-typed [Double] value instead. This + * You should usually call [Builder.start] with a well-typed [Float] value instead. This * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun start(start: JsonField) = apply { this.start = start } + fun start(start: JsonField) = apply { this.start = start } /** The text content of the word. */ fun word(word: String) = word(JsonField.of(word)) diff --git a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchCancelParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchCancelParams.kt index c9a0e725..4fd0d88f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchCancelParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchCancelParams.kt @@ -4,12 +4,12 @@ package com.openai.models.batches import com.openai.core.JsonValue 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 +import kotlin.jvm.optionals.getOrNull /** * Cancels an in-progress batch. The batch will be in status `cancelling` for up to 10 minutes, @@ -18,13 +18,13 @@ import java.util.Optional */ class BatchCancelParams private constructor( - private val batchId: String, + private val batchId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, ) : Params { - fun batchId(): String = batchId + fun batchId(): Optional = Optional.ofNullable(batchId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -36,14 +36,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [BatchCancelParams]. - * - * The following fields are required: - * ```java - * .batchId() - * ``` - */ + @JvmStatic fun none(): BatchCancelParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [BatchCancelParams]. */ @JvmStatic fun builder() = Builder() } @@ -63,7 +58,10 @@ private constructor( additionalBodyProperties = batchCancelParams.additionalBodyProperties.toMutableMap() } - fun batchId(batchId: String) = apply { this.batchId = batchId } + fun batchId(batchId: String?) = apply { this.batchId = batchId } + + /** Alias for calling [Builder.batchId] with `batchId.orElse(null)`. */ + fun batchId(batchId: Optional) = batchId(batchId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -189,17 +187,10 @@ private constructor( * Returns an immutable instance of [BatchCancelParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .batchId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): BatchCancelParams = BatchCancelParams( - checkRequired("batchId", batchId), + batchId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -211,7 +202,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> batchId + 0 -> batchId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPage.kt index 35c7aa3f..d591ebcc 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPage.kt @@ -2,12 +2,12 @@ package com.openai.models.batches +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.BatchService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [BatchService.list] */ @@ -16,7 +16,7 @@ private constructor( private val service: BatchService, private val params: BatchListParams, private val response: BatchListPageResponse, -) { +) : Page { /** * Delegates to [BatchListPageResponse], but gracefully handles missing data. @@ -32,19 +32,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): BatchListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + override fun nextPage(): BatchListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): BatchListParams = params @@ -113,25 +110,6 @@ private constructor( ) } - class AutoPager(private val firstPage: BatchListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPageAsync.kt index 61612712..0b36c622 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPageAsync.kt @@ -2,22 +2,24 @@ package com.openai.models.batches +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.BatchServiceAsync import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [BatchServiceAsync.list] */ class BatchListPageAsync private constructor( private val service: BatchServiceAsync, + private val streamHandlerExecutor: Executor, private val params: BatchListParams, private val response: BatchListPageResponse, -) { +) : PageAsync { /** * Delegates to [BatchListPageResponse], but gracefully handles missing data. @@ -33,22 +35,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): BatchListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): BatchListParams = params @@ -66,6 +62,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -77,18 +74,24 @@ private constructor( class Builder internal constructor() { private var service: BatchServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: BatchListParams? = null private var response: BatchListPageResponse? = null @JvmSynthetic internal fun from(batchListPageAsync: BatchListPageAsync) = apply { service = batchListPageAsync.service + streamHandlerExecutor = batchListPageAsync.streamHandlerExecutor params = batchListPageAsync.params response = batchListPageAsync.response } fun service(service: BatchServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: BatchListParams) = apply { this.params = params } @@ -103,6 +106,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -112,47 +116,22 @@ private constructor( fun build(): BatchListPageAsync = BatchListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: BatchListPageAsync) { - - fun forEach(action: Predicate, executor: Executor): CompletableFuture { - fun CompletableFuture>.forEach( - action: (Batch) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is BatchListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is BatchListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "BatchListPageAsync{service=$service, params=$params, response=$response}" + "BatchListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchRetrieveParams.kt index 94571a8a..d1667cc1 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchRetrieveParams.kt @@ -3,20 +3,21 @@ package com.openai.models.batches import com.openai.core.Params -import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Retrieves a batch. */ class BatchRetrieveParams private constructor( - private val batchId: String, + private val batchId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun batchId(): String = batchId + fun batchId(): Optional = Optional.ofNullable(batchId) fun _additionalHeaders(): Headers = additionalHeaders @@ -26,14 +27,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [BatchRetrieveParams]. - * - * The following fields are required: - * ```java - * .batchId() - * ``` - */ + @JvmStatic fun none(): BatchRetrieveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [BatchRetrieveParams]. */ @JvmStatic fun builder() = Builder() } @@ -51,7 +47,10 @@ private constructor( additionalQueryParams = batchRetrieveParams.additionalQueryParams.toBuilder() } - fun batchId(batchId: String) = apply { this.batchId = batchId } + fun batchId(batchId: String?) = apply { this.batchId = batchId } + + /** Alias for calling [Builder.batchId] with `batchId.orElse(null)`. */ + fun batchId(batchId: Optional) = batchId(batchId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -155,25 +154,14 @@ private constructor( * Returns an immutable instance of [BatchRetrieveParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .batchId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): BatchRetrieveParams = - BatchRetrieveParams( - checkRequired("batchId", batchId), - additionalHeaders.build(), - additionalQueryParams.build(), - ) + BatchRetrieveParams(batchId, additionalHeaders.build(), additionalQueryParams.build()) } fun _pathParam(index: Int): String = when (index) { - 0 -> batchId + 0 -> batchId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantDeleteParams.kt index f0c6d40f..b5df03cb 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantDeleteParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantDeleteParams.kt @@ -4,23 +4,23 @@ package com.openai.models.beta.assistants import com.openai.core.JsonValue 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 +import kotlin.jvm.optionals.getOrNull /** Delete an assistant. */ class AssistantDeleteParams private constructor( - private val assistantId: String, + private val assistantId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, ) : Params { - fun assistantId(): String = assistantId + fun assistantId(): Optional = Optional.ofNullable(assistantId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -32,14 +32,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [AssistantDeleteParams]. - * - * The following fields are required: - * ```java - * .assistantId() - * ``` - */ + @JvmStatic fun none(): AssistantDeleteParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [AssistantDeleteParams]. */ @JvmStatic fun builder() = Builder() } @@ -59,7 +54,10 @@ private constructor( additionalBodyProperties = assistantDeleteParams.additionalBodyProperties.toMutableMap() } - fun assistantId(assistantId: String) = apply { this.assistantId = assistantId } + fun assistantId(assistantId: String?) = apply { this.assistantId = assistantId } + + /** Alias for calling [Builder.assistantId] with `assistantId.orElse(null)`. */ + fun assistantId(assistantId: Optional) = assistantId(assistantId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -185,17 +183,10 @@ private constructor( * Returns an immutable instance of [AssistantDeleteParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .assistantId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): AssistantDeleteParams = AssistantDeleteParams( - checkRequired("assistantId", assistantId), + assistantId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -207,7 +198,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> assistantId + 0 -> assistantId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPage.kt index c852b6fe..596b6fa6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPage.kt @@ -2,12 +2,12 @@ package com.openai.models.beta.assistants +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.beta.AssistantService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [AssistantService.list] */ @@ -16,7 +16,7 @@ private constructor( private val service: AssistantService, private val params: AssistantListParams, private val response: AssistantListPageResponse, -) { +) : Page { /** * Delegates to [AssistantListPageResponse], but gracefully handles missing data. @@ -32,19 +32,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): AssistantListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + override fun nextPage(): AssistantListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): AssistantListParams = params @@ -113,25 +110,6 @@ private constructor( ) } - class AutoPager(private val firstPage: AssistantListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPageAsync.kt index 0e90b27c..ce42fb78 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPageAsync.kt @@ -2,22 +2,24 @@ package com.openai.models.beta.assistants +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.beta.AssistantServiceAsync import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [AssistantServiceAsync.list] */ class AssistantListPageAsync private constructor( private val service: AssistantServiceAsync, + private val streamHandlerExecutor: Executor, private val params: AssistantListParams, private val response: AssistantListPageResponse, -) { +) : PageAsync { /** * Delegates to [AssistantListPageResponse], but gracefully handles missing data. @@ -33,22 +35,17 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): AssistantListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = + service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): AssistantListParams = params @@ -66,6 +63,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -77,18 +75,24 @@ private constructor( class Builder internal constructor() { private var service: AssistantServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: AssistantListParams? = null private var response: AssistantListPageResponse? = null @JvmSynthetic internal fun from(assistantListPageAsync: AssistantListPageAsync) = apply { service = assistantListPageAsync.service + streamHandlerExecutor = assistantListPageAsync.streamHandlerExecutor params = assistantListPageAsync.params response = assistantListPageAsync.response } fun service(service: AssistantServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: AssistantListParams) = apply { this.params = params } @@ -103,6 +107,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -112,47 +117,22 @@ private constructor( fun build(): AssistantListPageAsync = AssistantListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: AssistantListPageAsync) { - - fun forEach(action: Predicate, executor: Executor): CompletableFuture { - fun CompletableFuture>.forEach( - action: (Assistant) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is AssistantListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is AssistantListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "AssistantListPageAsync{service=$service, params=$params, response=$response}" + "AssistantListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantRetrieveParams.kt index 5ded97d8..7b9b0792 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantRetrieveParams.kt @@ -3,20 +3,21 @@ package com.openai.models.beta.assistants import com.openai.core.Params -import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Retrieves an assistant. */ class AssistantRetrieveParams private constructor( - private val assistantId: String, + private val assistantId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun assistantId(): String = assistantId + fun assistantId(): Optional = Optional.ofNullable(assistantId) fun _additionalHeaders(): Headers = additionalHeaders @@ -26,14 +27,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [AssistantRetrieveParams]. - * - * The following fields are required: - * ```java - * .assistantId() - * ``` - */ + @JvmStatic fun none(): AssistantRetrieveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [AssistantRetrieveParams]. */ @JvmStatic fun builder() = Builder() } @@ -51,7 +47,10 @@ private constructor( additionalQueryParams = assistantRetrieveParams.additionalQueryParams.toBuilder() } - fun assistantId(assistantId: String) = apply { this.assistantId = assistantId } + fun assistantId(assistantId: String?) = apply { this.assistantId = assistantId } + + /** Alias for calling [Builder.assistantId] with `assistantId.orElse(null)`. */ + fun assistantId(assistantId: Optional) = assistantId(assistantId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -155,17 +154,10 @@ private constructor( * Returns an immutable instance of [AssistantRetrieveParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .assistantId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): AssistantRetrieveParams = AssistantRetrieveParams( - checkRequired("assistantId", assistantId), + assistantId, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -173,7 +165,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> assistantId + 0 -> assistantId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantUpdateParams.kt index 92fcba31..24ae8550 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantUpdateParams.kt @@ -13,7 +13,6 @@ import com.openai.core.JsonMissing import com.openai.core.JsonValue 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 @@ -32,13 +31,13 @@ import kotlin.jvm.optionals.getOrNull /** Modifies an assistant. */ class AssistantUpdateParams private constructor( - private val assistantId: String, + private val assistantId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun assistantId(): String = assistantId + fun assistantId(): Optional = Optional.ofNullable(assistantId) /** * The description of the assistant. The maximum length is 512 characters. @@ -253,14 +252,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [AssistantUpdateParams]. - * - * The following fields are required: - * ```java - * .assistantId() - * ``` - */ + @JvmStatic fun none(): AssistantUpdateParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [AssistantUpdateParams]. */ @JvmStatic fun builder() = Builder() } @@ -280,7 +274,10 @@ private constructor( additionalQueryParams = assistantUpdateParams.additionalQueryParams.toBuilder() } - fun assistantId(assistantId: String) = apply { this.assistantId = assistantId } + fun assistantId(assistantId: String?) = apply { this.assistantId = assistantId } + + /** Alias for calling [Builder.assistantId] with `assistantId.orElse(null)`. */ + fun assistantId(assistantId: Optional) = assistantId(assistantId.getOrNull()) /** * Sets the entire request body. @@ -723,17 +720,10 @@ private constructor( * Returns an immutable instance of [AssistantUpdateParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .assistantId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): AssistantUpdateParams = AssistantUpdateParams( - checkRequired("assistantId", assistantId), + assistantId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -744,7 +734,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> assistantId + 0 -> assistantId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadDeleteParams.kt index fd960288..637a5853 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadDeleteParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadDeleteParams.kt @@ -4,23 +4,23 @@ package com.openai.models.beta.threads import com.openai.core.JsonValue 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 +import kotlin.jvm.optionals.getOrNull /** Delete a thread. */ class ThreadDeleteParams private constructor( - private val threadId: String, + private val threadId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, ) : Params { - fun threadId(): String = threadId + fun threadId(): Optional = Optional.ofNullable(threadId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -32,14 +32,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [ThreadDeleteParams]. - * - * The following fields are required: - * ```java - * .threadId() - * ``` - */ + @JvmStatic fun none(): ThreadDeleteParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [ThreadDeleteParams]. */ @JvmStatic fun builder() = Builder() } @@ -59,7 +54,10 @@ private constructor( additionalBodyProperties = threadDeleteParams.additionalBodyProperties.toMutableMap() } - fun threadId(threadId: String) = apply { this.threadId = threadId } + fun threadId(threadId: String?) = apply { this.threadId = threadId } + + /** Alias for calling [Builder.threadId] with `threadId.orElse(null)`. */ + fun threadId(threadId: Optional) = threadId(threadId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -185,17 +183,10 @@ private constructor( * Returns an immutable instance of [ThreadDeleteParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .threadId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): ThreadDeleteParams = ThreadDeleteParams( - checkRequired("threadId", threadId), + threadId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -207,7 +198,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> threadId + 0 -> threadId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadRetrieveParams.kt index 1d5bb1ae..2ee637c6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadRetrieveParams.kt @@ -3,20 +3,21 @@ package com.openai.models.beta.threads import com.openai.core.Params -import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Retrieves a thread. */ class ThreadRetrieveParams private constructor( - private val threadId: String, + private val threadId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun threadId(): String = threadId + fun threadId(): Optional = Optional.ofNullable(threadId) fun _additionalHeaders(): Headers = additionalHeaders @@ -26,14 +27,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [ThreadRetrieveParams]. - * - * The following fields are required: - * ```java - * .threadId() - * ``` - */ + @JvmStatic fun none(): ThreadRetrieveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [ThreadRetrieveParams]. */ @JvmStatic fun builder() = Builder() } @@ -51,7 +47,10 @@ private constructor( additionalQueryParams = threadRetrieveParams.additionalQueryParams.toBuilder() } - fun threadId(threadId: String) = apply { this.threadId = threadId } + fun threadId(threadId: String?) = apply { this.threadId = threadId } + + /** Alias for calling [Builder.threadId] with `threadId.orElse(null)`. */ + fun threadId(threadId: Optional) = threadId(threadId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -155,25 +154,14 @@ private constructor( * Returns an immutable instance of [ThreadRetrieveParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .threadId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): ThreadRetrieveParams = - ThreadRetrieveParams( - checkRequired("threadId", threadId), - additionalHeaders.build(), - additionalQueryParams.build(), - ) + ThreadRetrieveParams(threadId, additionalHeaders.build(), additionalQueryParams.build()) } fun _pathParam(index: Int): String = when (index) { - 0 -> threadId + 0 -> threadId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadUpdateParams.kt index 3afece77..d6d639af 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/ThreadUpdateParams.kt @@ -12,7 +12,6 @@ import com.openai.core.JsonMissing import com.openai.core.JsonValue 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 @@ -25,13 +24,13 @@ import kotlin.jvm.optionals.getOrNull /** Modifies a thread. */ class ThreadUpdateParams private constructor( - private val threadId: String, + private val threadId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun threadId(): String = threadId + fun threadId(): Optional = Optional.ofNullable(threadId) /** * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing @@ -80,14 +79,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [ThreadUpdateParams]. - * - * The following fields are required: - * ```java - * .threadId() - * ``` - */ + @JvmStatic fun none(): ThreadUpdateParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [ThreadUpdateParams]. */ @JvmStatic fun builder() = Builder() } @@ -107,7 +101,10 @@ private constructor( additionalQueryParams = threadUpdateParams.additionalQueryParams.toBuilder() } - fun threadId(threadId: String) = apply { this.threadId = threadId } + fun threadId(threadId: String?) = apply { this.threadId = threadId } + + /** Alias for calling [Builder.threadId] with `threadId.orElse(null)`. */ + fun threadId(threadId: Optional) = threadId(threadId.getOrNull()) /** * Sets the entire request body. @@ -287,17 +284,10 @@ private constructor( * Returns an immutable instance of [ThreadUpdateParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .threadId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): ThreadUpdateParams = ThreadUpdateParams( - checkRequired("threadId", threadId), + threadId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -308,7 +298,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> threadId + 0 -> threadId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageCreateParams.kt index 6bf8f054..caca9be5 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageCreateParams.kt @@ -38,13 +38,13 @@ import kotlin.jvm.optionals.getOrNull /** Create a message. */ class MessageCreateParams private constructor( - private val threadId: String, + private val threadId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun threadId(): String = threadId + fun threadId(): Optional = Optional.ofNullable(threadId) /** * The text contents of the message. @@ -130,7 +130,6 @@ private constructor( * * The following fields are required: * ```java - * .threadId() * .content() * .role() * ``` @@ -154,7 +153,10 @@ private constructor( additionalQueryParams = messageCreateParams.additionalQueryParams.toBuilder() } - fun threadId(threadId: String) = apply { this.threadId = threadId } + fun threadId(threadId: String?) = apply { this.threadId = threadId } + + /** Alias for calling [Builder.threadId] with `threadId.orElse(null)`. */ + fun threadId(threadId: Optional) = threadId(threadId.getOrNull()) /** * Sets the entire request body. @@ -378,7 +380,6 @@ private constructor( * * The following fields are required: * ```java - * .threadId() * .content() * .role() * ``` @@ -387,7 +388,7 @@ private constructor( */ fun build(): MessageCreateParams = MessageCreateParams( - checkRequired("threadId", threadId), + threadId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -398,7 +399,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> threadId + 0 -> threadId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDeleteParams.kt index e2022510..9c79b7b3 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDeleteParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageDeleteParams.kt @@ -10,12 +10,13 @@ import com.openai.core.http.QueryParams import com.openai.core.toImmutable import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Deletes a message. */ class MessageDeleteParams private constructor( private val threadId: String, - private val messageId: String, + private val messageId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, @@ -23,7 +24,7 @@ private constructor( fun threadId(): String = threadId - fun messageId(): String = messageId + fun messageId(): Optional = Optional.ofNullable(messageId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -41,7 +42,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .messageId() * ``` */ @JvmStatic fun builder() = Builder() @@ -67,7 +67,10 @@ private constructor( fun threadId(threadId: String) = apply { this.threadId = threadId } - fun messageId(messageId: String) = apply { this.messageId = messageId } + fun messageId(messageId: String?) = apply { this.messageId = messageId } + + /** Alias for calling [Builder.messageId] with `messageId.orElse(null)`. */ + fun messageId(messageId: Optional) = messageId(messageId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -197,7 +200,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .messageId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -205,7 +207,7 @@ private constructor( fun build(): MessageDeleteParams = MessageDeleteParams( checkRequired("threadId", threadId), - checkRequired("messageId", messageId), + messageId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -218,7 +220,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> threadId - 1 -> messageId + 1 -> messageId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPage.kt index 14a9a44d..560ff950 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPage.kt @@ -2,12 +2,12 @@ package com.openai.models.beta.threads.messages +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.beta.threads.MessageService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [MessageService.list] */ @@ -16,7 +16,7 @@ private constructor( private val service: MessageService, private val params: MessageListParams, private val response: MessageListPageResponse, -) { +) : Page { /** * Delegates to [MessageListPageResponse], but gracefully handles missing data. @@ -32,19 +32,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): MessageListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + override fun nextPage(): MessageListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): MessageListParams = params @@ -113,25 +110,6 @@ private constructor( ) } - class AutoPager(private val firstPage: MessageListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPageAsync.kt index fd1595e3..8b6a0d4e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPageAsync.kt @@ -2,22 +2,24 @@ package com.openai.models.beta.threads.messages +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.beta.threads.MessageServiceAsync import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [MessageServiceAsync.list] */ class MessageListPageAsync private constructor( private val service: MessageServiceAsync, + private val streamHandlerExecutor: Executor, private val params: MessageListParams, private val response: MessageListPageResponse, -) { +) : PageAsync { /** * Delegates to [MessageListPageResponse], but gracefully handles missing data. @@ -33,22 +35,17 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): MessageListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = + service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): MessageListParams = params @@ -66,6 +63,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -77,18 +75,24 @@ private constructor( class Builder internal constructor() { private var service: MessageServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: MessageListParams? = null private var response: MessageListPageResponse? = null @JvmSynthetic internal fun from(messageListPageAsync: MessageListPageAsync) = apply { service = messageListPageAsync.service + streamHandlerExecutor = messageListPageAsync.streamHandlerExecutor params = messageListPageAsync.params response = messageListPageAsync.response } fun service(service: MessageServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: MessageListParams) = apply { this.params = params } @@ -103,6 +107,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -112,47 +117,22 @@ private constructor( fun build(): MessageListPageAsync = MessageListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: MessageListPageAsync) { - - fun forEach(action: Predicate, executor: Executor): CompletableFuture { - fun CompletableFuture>.forEach( - action: (Message) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is MessageListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is MessageListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "MessageListPageAsync{service=$service, params=$params, response=$response}" + "MessageListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListParams.kt index 0a1f8bbb..add2694a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListParams.kt @@ -6,7 +6,6 @@ import com.fasterxml.jackson.annotation.JsonCreator import com.openai.core.Enum import com.openai.core.JsonField 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.errors.OpenAIInvalidDataException @@ -17,7 +16,7 @@ import kotlin.jvm.optionals.getOrNull /** Returns a list of messages for a given thread. */ class MessageListParams private constructor( - private val threadId: String, + private val threadId: String?, private val after: String?, private val before: String?, private val limit: Long?, @@ -27,7 +26,7 @@ private constructor( private val additionalQueryParams: QueryParams, ) : Params { - fun threadId(): String = threadId + fun threadId(): Optional = Optional.ofNullable(threadId) /** * A cursor for use in pagination. `after` is an object ID that defines your place in the list. @@ -66,14 +65,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [MessageListParams]. - * - * The following fields are required: - * ```java - * .threadId() - * ``` - */ + @JvmStatic fun none(): MessageListParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [MessageListParams]. */ @JvmStatic fun builder() = Builder() } @@ -101,7 +95,10 @@ private constructor( additionalQueryParams = messageListParams.additionalQueryParams.toBuilder() } - fun threadId(threadId: String) = apply { this.threadId = threadId } + fun threadId(threadId: String?) = apply { this.threadId = threadId } + + /** Alias for calling [Builder.threadId] with `threadId.orElse(null)`. */ + fun threadId(threadId: Optional) = threadId(threadId.getOrNull()) /** * A cursor for use in pagination. `after` is an object ID that defines your place in the @@ -258,17 +255,10 @@ private constructor( * Returns an immutable instance of [MessageListParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .threadId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): MessageListParams = MessageListParams( - checkRequired("threadId", threadId), + threadId, after, before, limit, @@ -281,7 +271,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> threadId + 0 -> threadId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageRetrieveParams.kt index fc521fbf..4c9cb093 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageRetrieveParams.kt @@ -7,19 +7,21 @@ import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Retrieve a message. */ class MessageRetrieveParams private constructor( private val threadId: String, - private val messageId: String, + private val messageId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun threadId(): String = threadId - fun messageId(): String = messageId + fun messageId(): Optional = Optional.ofNullable(messageId) fun _additionalHeaders(): Headers = additionalHeaders @@ -35,7 +37,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .messageId() * ``` */ @JvmStatic fun builder() = Builder() @@ -59,7 +60,10 @@ private constructor( fun threadId(threadId: String) = apply { this.threadId = threadId } - fun messageId(messageId: String) = apply { this.messageId = messageId } + fun messageId(messageId: String?) = apply { this.messageId = messageId } + + /** Alias for calling [Builder.messageId] with `messageId.orElse(null)`. */ + fun messageId(messageId: Optional) = messageId(messageId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -167,7 +171,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .messageId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -175,7 +178,7 @@ private constructor( fun build(): MessageRetrieveParams = MessageRetrieveParams( checkRequired("threadId", threadId), - checkRequired("messageId", messageId), + messageId, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -184,7 +187,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> threadId - 1 -> messageId + 1 -> messageId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageUpdateParams.kt index de35e85e..30271871 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageUpdateParams.kt @@ -25,7 +25,7 @@ import kotlin.jvm.optionals.getOrNull class MessageUpdateParams private constructor( private val threadId: String, - private val messageId: String, + private val messageId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, @@ -33,7 +33,7 @@ private constructor( fun threadId(): String = threadId - fun messageId(): String = messageId + fun messageId(): Optional = Optional.ofNullable(messageId) /** * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing @@ -71,7 +71,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .messageId() * ``` */ @JvmStatic fun builder() = Builder() @@ -97,7 +96,10 @@ private constructor( fun threadId(threadId: String) = apply { this.threadId = threadId } - fun messageId(messageId: String) = apply { this.messageId = messageId } + fun messageId(messageId: String?) = apply { this.messageId = messageId } + + /** Alias for calling [Builder.messageId] with `messageId.orElse(null)`. */ + fun messageId(messageId: Optional) = messageId(messageId.getOrNull()) /** * Sets the entire request body. @@ -255,7 +257,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .messageId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -263,7 +264,7 @@ private constructor( fun build(): MessageUpdateParams = MessageUpdateParams( checkRequired("threadId", threadId), - checkRequired("messageId", messageId), + messageId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -275,7 +276,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> threadId - 1 -> messageId + 1 -> messageId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunCancelParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunCancelParams.kt index cf442b6c..ccb81100 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunCancelParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunCancelParams.kt @@ -10,12 +10,13 @@ import com.openai.core.http.QueryParams import com.openai.core.toImmutable import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Cancels a run that is `in_progress`. */ class RunCancelParams private constructor( private val threadId: String, - private val runId: String, + private val runId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, @@ -23,7 +24,7 @@ private constructor( fun threadId(): String = threadId - fun runId(): String = runId + fun runId(): Optional = Optional.ofNullable(runId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -41,7 +42,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .runId() * ``` */ @JvmStatic fun builder() = Builder() @@ -67,7 +67,10 @@ private constructor( fun threadId(threadId: String) = apply { this.threadId = threadId } - fun runId(runId: String) = apply { this.runId = runId } + fun runId(runId: String?) = apply { this.runId = runId } + + /** Alias for calling [Builder.runId] with `runId.orElse(null)`. */ + fun runId(runId: Optional) = runId(runId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -197,7 +200,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .runId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -205,7 +207,7 @@ private constructor( fun build(): RunCancelParams = RunCancelParams( checkRequired("threadId", threadId), - checkRequired("runId", runId), + runId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -218,7 +220,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> threadId - 1 -> runId + 1 -> runId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunCreateParams.kt index 3d44da70..02f16270 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunCreateParams.kt @@ -52,14 +52,14 @@ import kotlin.jvm.optionals.getOrNull /** Create a run. */ class RunCreateParams private constructor( - private val threadId: String, + private val threadId: String?, private val include: List?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun threadId(): String = threadId + fun threadId(): Optional = Optional.ofNullable(threadId) /** * A list of additional fields to include in the response. Currently the only supported value is @@ -385,7 +385,6 @@ private constructor( * * The following fields are required: * ```java - * .threadId() * .assistantId() * ``` */ @@ -410,7 +409,10 @@ private constructor( additionalQueryParams = runCreateParams.additionalQueryParams.toBuilder() } - fun threadId(threadId: String) = apply { this.threadId = threadId } + fun threadId(threadId: String?) = apply { this.threadId = threadId } + + /** Alias for calling [Builder.threadId] with `threadId.orElse(null)`. */ + fun threadId(threadId: Optional) = threadId(threadId.getOrNull()) /** * A list of additional fields to include in the response. Currently the only supported @@ -1057,7 +1059,6 @@ private constructor( * * The following fields are required: * ```java - * .threadId() * .assistantId() * ``` * @@ -1065,7 +1066,7 @@ private constructor( */ fun build(): RunCreateParams = RunCreateParams( - checkRequired("threadId", threadId), + threadId, include?.toImmutable(), body.build(), additionalHeaders.build(), @@ -1077,7 +1078,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> threadId + 0 -> threadId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPage.kt index 108f1287..206a0e6a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPage.kt @@ -2,12 +2,12 @@ package com.openai.models.beta.threads.runs +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.beta.threads.RunService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [RunService.list] */ @@ -16,7 +16,7 @@ private constructor( private val service: RunService, private val params: RunListParams, private val response: RunListPageResponse, -) { +) : Page { /** * Delegates to [RunListPageResponse], but gracefully handles missing data. @@ -32,19 +32,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): RunListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + override fun nextPage(): RunListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): RunListParams = params @@ -113,25 +110,6 @@ private constructor( ) } - class AutoPager(private val firstPage: RunListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPageAsync.kt index ca329d17..e5e70466 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPageAsync.kt @@ -2,22 +2,24 @@ package com.openai.models.beta.threads.runs +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.beta.threads.RunServiceAsync import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [RunServiceAsync.list] */ class RunListPageAsync private constructor( private val service: RunServiceAsync, + private val streamHandlerExecutor: Executor, private val params: RunListParams, private val response: RunListPageResponse, -) { +) : PageAsync { /** * Delegates to [RunListPageResponse], but gracefully handles missing data. @@ -33,22 +35,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): RunListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): RunListParams = params @@ -66,6 +62,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -77,18 +74,24 @@ private constructor( class Builder internal constructor() { private var service: RunServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: RunListParams? = null private var response: RunListPageResponse? = null @JvmSynthetic internal fun from(runListPageAsync: RunListPageAsync) = apply { service = runListPageAsync.service + streamHandlerExecutor = runListPageAsync.streamHandlerExecutor params = runListPageAsync.params response = runListPageAsync.response } fun service(service: RunServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: RunListParams) = apply { this.params = params } @@ -103,6 +106,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -112,47 +116,22 @@ private constructor( fun build(): RunListPageAsync = RunListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: RunListPageAsync) { - - fun forEach(action: Predicate, executor: Executor): CompletableFuture { - fun CompletableFuture>.forEach( - action: (Run) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is RunListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is RunListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "RunListPageAsync{service=$service, params=$params, response=$response}" + "RunListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListParams.kt index 22d96714..cd4e0d5d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListParams.kt @@ -6,7 +6,6 @@ import com.fasterxml.jackson.annotation.JsonCreator import com.openai.core.Enum import com.openai.core.JsonField 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.errors.OpenAIInvalidDataException @@ -17,7 +16,7 @@ import kotlin.jvm.optionals.getOrNull /** Returns a list of runs belonging to a thread. */ class RunListParams private constructor( - private val threadId: String, + private val threadId: String?, private val after: String?, private val before: String?, private val limit: Long?, @@ -26,7 +25,7 @@ private constructor( private val additionalQueryParams: QueryParams, ) : Params { - fun threadId(): String = threadId + fun threadId(): Optional = Optional.ofNullable(threadId) /** * A cursor for use in pagination. `after` is an object ID that defines your place in the list. @@ -62,14 +61,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [RunListParams]. - * - * The following fields are required: - * ```java - * .threadId() - * ``` - */ + @JvmStatic fun none(): RunListParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [RunListParams]. */ @JvmStatic fun builder() = Builder() } @@ -95,7 +89,10 @@ private constructor( additionalQueryParams = runListParams.additionalQueryParams.toBuilder() } - fun threadId(threadId: String) = apply { this.threadId = threadId } + fun threadId(threadId: String?) = apply { this.threadId = threadId } + + /** Alias for calling [Builder.threadId] with `threadId.orElse(null)`. */ + fun threadId(threadId: Optional) = threadId(threadId.getOrNull()) /** * A cursor for use in pagination. `after` is an object ID that defines your place in the @@ -246,17 +243,10 @@ private constructor( * Returns an immutable instance of [RunListParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .threadId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): RunListParams = RunListParams( - checkRequired("threadId", threadId), + threadId, after, before, limit, @@ -268,7 +258,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> threadId + 0 -> threadId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunRetrieveParams.kt index 850f086c..1ffc8e41 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunRetrieveParams.kt @@ -7,19 +7,21 @@ import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Retrieves a run. */ class RunRetrieveParams private constructor( private val threadId: String, - private val runId: String, + private val runId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun threadId(): String = threadId - fun runId(): String = runId + fun runId(): Optional = Optional.ofNullable(runId) fun _additionalHeaders(): Headers = additionalHeaders @@ -35,7 +37,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .runId() * ``` */ @JvmStatic fun builder() = Builder() @@ -59,7 +60,10 @@ private constructor( fun threadId(threadId: String) = apply { this.threadId = threadId } - fun runId(runId: String) = apply { this.runId = runId } + fun runId(runId: String?) = apply { this.runId = runId } + + /** Alias for calling [Builder.runId] with `runId.orElse(null)`. */ + fun runId(runId: Optional) = runId(runId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -167,7 +171,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .runId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -175,7 +178,7 @@ private constructor( fun build(): RunRetrieveParams = RunRetrieveParams( checkRequired("threadId", threadId), - checkRequired("runId", runId), + runId, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -184,7 +187,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> threadId - 1 -> runId + 1 -> runId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunSubmitToolOutputsParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunSubmitToolOutputsParams.kt index 38e05998..8b23d6c9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunSubmitToolOutputsParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunSubmitToolOutputsParams.kt @@ -30,7 +30,7 @@ import kotlin.jvm.optionals.getOrNull class RunSubmitToolOutputsParams private constructor( private val threadId: String, - private val runId: String, + private val runId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, @@ -38,7 +38,7 @@ private constructor( fun threadId(): String = threadId - fun runId(): String = runId + fun runId(): Optional = Optional.ofNullable(runId) /** * A list of tools for which the outputs are being submitted. @@ -71,7 +71,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .runId() * .toolOutputs() * ``` */ @@ -98,7 +97,10 @@ private constructor( fun threadId(threadId: String) = apply { this.threadId = threadId } - fun runId(runId: String) = apply { this.runId = runId } + fun runId(runId: String?) = apply { this.runId = runId } + + /** Alias for calling [Builder.runId] with `runId.orElse(null)`. */ + fun runId(runId: Optional) = runId(runId.getOrNull()) /** * Sets the entire request body. @@ -255,7 +257,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .runId() * .toolOutputs() * ``` * @@ -264,7 +265,7 @@ private constructor( fun build(): RunSubmitToolOutputsParams = RunSubmitToolOutputsParams( checkRequired("threadId", threadId), - checkRequired("runId", runId), + runId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -276,7 +277,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> threadId - 1 -> runId + 1 -> runId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunUpdateParams.kt index b93a1235..21ecf31a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunUpdateParams.kt @@ -25,7 +25,7 @@ import kotlin.jvm.optionals.getOrNull class RunUpdateParams private constructor( private val threadId: String, - private val runId: String, + private val runId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, @@ -33,7 +33,7 @@ private constructor( fun threadId(): String = threadId - fun runId(): String = runId + fun runId(): Optional = Optional.ofNullable(runId) /** * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing @@ -71,7 +71,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .runId() * ``` */ @JvmStatic fun builder() = Builder() @@ -97,7 +96,10 @@ private constructor( fun threadId(threadId: String) = apply { this.threadId = threadId } - fun runId(runId: String) = apply { this.runId = runId } + fun runId(runId: String?) = apply { this.runId = runId } + + /** Alias for calling [Builder.runId] with `runId.orElse(null)`. */ + fun runId(runId: Optional) = runId(runId.getOrNull()) /** * Sets the entire request body. @@ -255,7 +257,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .runId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -263,7 +264,7 @@ private constructor( fun build(): RunUpdateParams = RunUpdateParams( checkRequired("threadId", threadId), - checkRequired("runId", runId), + runId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -275,7 +276,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> threadId - 1 -> runId + 1 -> runId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPage.kt index b9740fab..353febb1 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPage.kt @@ -2,12 +2,12 @@ package com.openai.models.beta.threads.runs.steps +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.beta.threads.runs.StepService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [StepService.list] */ @@ -16,7 +16,7 @@ private constructor( private val service: StepService, private val params: StepListParams, private val response: StepListPageResponse, -) { +) : Page { /** * Delegates to [StepListPageResponse], but gracefully handles missing data. @@ -32,19 +32,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): StepListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + override fun nextPage(): StepListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): StepListParams = params @@ -113,25 +110,6 @@ private constructor( ) } - class AutoPager(private val firstPage: StepListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPageAsync.kt index a753633e..8e5b3009 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPageAsync.kt @@ -2,22 +2,24 @@ package com.openai.models.beta.threads.runs.steps +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.beta.threads.runs.StepServiceAsync import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [StepServiceAsync.list] */ class StepListPageAsync private constructor( private val service: StepServiceAsync, + private val streamHandlerExecutor: Executor, private val params: StepListParams, private val response: StepListPageResponse, -) { +) : PageAsync { /** * Delegates to [StepListPageResponse], but gracefully handles missing data. @@ -33,22 +35,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): StepListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): StepListParams = params @@ -66,6 +62,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -77,18 +74,24 @@ private constructor( class Builder internal constructor() { private var service: StepServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: StepListParams? = null private var response: StepListPageResponse? = null @JvmSynthetic internal fun from(stepListPageAsync: StepListPageAsync) = apply { service = stepListPageAsync.service + streamHandlerExecutor = stepListPageAsync.streamHandlerExecutor params = stepListPageAsync.params response = stepListPageAsync.response } fun service(service: StepServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: StepListParams) = apply { this.params = params } @@ -103,6 +106,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -112,47 +116,22 @@ private constructor( fun build(): StepListPageAsync = StepListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: StepListPageAsync) { - - fun forEach(action: Predicate, executor: Executor): CompletableFuture { - fun CompletableFuture>.forEach( - action: (RunStep) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is StepListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is StepListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "StepListPageAsync{service=$service, params=$params, response=$response}" + "StepListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListParams.kt index ca18dea9..c88cb3ef 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListParams.kt @@ -19,7 +19,7 @@ import kotlin.jvm.optionals.getOrNull class StepListParams private constructor( private val threadId: String, - private val runId: String, + private val runId: String?, private val after: String?, private val before: String?, private val include: List?, @@ -31,7 +31,7 @@ private constructor( fun threadId(): String = threadId - fun runId(): String = runId + fun runId(): Optional = Optional.ofNullable(runId) /** * A cursor for use in pagination. `after` is an object ID that defines your place in the list. @@ -84,7 +84,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .runId() * ``` */ @JvmStatic fun builder() = Builder() @@ -118,7 +117,10 @@ private constructor( fun threadId(threadId: String) = apply { this.threadId = threadId } - fun runId(runId: String) = apply { this.runId = runId } + fun runId(runId: String?) = apply { this.runId = runId } + + /** Alias for calling [Builder.runId] with `runId.orElse(null)`. */ + fun runId(runId: Optional) = runId(runId.getOrNull()) /** * A cursor for use in pagination. `after` is an object ID that defines your place in the @@ -298,7 +300,6 @@ private constructor( * The following fields are required: * ```java * .threadId() - * .runId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -306,7 +307,7 @@ private constructor( fun build(): StepListParams = StepListParams( checkRequired("threadId", threadId), - checkRequired("runId", runId), + runId, after, before, include?.toImmutable(), @@ -320,7 +321,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> threadId - 1 -> runId + 1 -> runId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepRetrieveParams.kt index 15275de5..c42a51a5 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepRetrieveParams.kt @@ -16,7 +16,7 @@ class StepRetrieveParams private constructor( private val threadId: String, private val runId: String, - private val stepId: String, + private val stepId: String?, private val include: List?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, @@ -26,7 +26,7 @@ private constructor( fun runId(): String = runId - fun stepId(): String = stepId + fun stepId(): Optional = Optional.ofNullable(stepId) /** * A list of additional fields to include in the response. Currently the only supported value is @@ -54,7 +54,6 @@ private constructor( * ```java * .threadId() * .runId() - * .stepId() * ``` */ @JvmStatic fun builder() = Builder() @@ -84,7 +83,10 @@ private constructor( fun runId(runId: String) = apply { this.runId = runId } - fun stepId(stepId: String) = apply { this.stepId = stepId } + fun stepId(stepId: String?) = apply { this.stepId = stepId } + + /** Alias for calling [Builder.stepId] with `stepId.orElse(null)`. */ + fun stepId(stepId: Optional) = stepId(stepId.getOrNull()) /** * A list of additional fields to include in the response. Currently the only supported @@ -218,7 +220,6 @@ private constructor( * ```java * .threadId() * .runId() - * .stepId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -227,7 +228,7 @@ private constructor( StepRetrieveParams( checkRequired("threadId", threadId), checkRequired("runId", runId), - checkRequired("stepId", stepId), + stepId, include?.toImmutable(), additionalHeaders.build(), additionalQueryParams.build(), @@ -238,7 +239,7 @@ private constructor( when (index) { 0 -> threadId 1 -> runId - 2 -> stepId + 2 -> stepId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParams.kt index a3281dc6..85d2582f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParams.kt @@ -19,6 +19,7 @@ import com.openai.core.Enum import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing +import com.openai.core.JsonSchemaLocalValidation import com.openai.core.JsonValue import com.openai.core.Params import com.openai.core.allMaxBy @@ -1297,6 +1298,31 @@ private constructor( body.responseFormat(jsonObject) } + /** + * Sets the response format to a JSON schema derived from the structure of the given class. + * This changes the builder to a type-safe [StructuredChatCompletionCreateParams.Builder] + * that will build a [StructuredChatCompletionCreateParams] instance when `build()` is + * called. + * + * @param responseType A class from which a JSON schema will be derived to define the + * response format. + * @param localValidation [com.openai.core.JsonSchemaLocalValidation.YES] (the default) to + * validate the JSON schema locally when it is generated by this method to confirm that it + * adheres to the requirements and restrictions on JSON schemas imposed by the OpenAI + * specification; or [com.openai.core.JsonSchemaLocalValidation.NO] to skip local + * validation and rely only on remote validation. See the SDK documentation for more + * details. + * @throws IllegalArgumentException If local validation is enabled, but it fails because a + * valid JSON schema cannot be derived from the given class. + */ + @JvmOverloads + fun responseFormat( + responseType: Class, + localValidation: JsonSchemaLocalValidation = JsonSchemaLocalValidation.YES, + ) = + StructuredChatCompletionCreateParams.builder() + .wrap(responseType, this, localValidation) + /** * This feature is in Beta. If specified, our system will make a best effort to sample * deterministically, such that repeated requests with the same `seed` and parameters should diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionDeleteParams.kt index 0cd4d7fa..64f6792e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionDeleteParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionDeleteParams.kt @@ -4,12 +4,12 @@ package com.openai.models.chat.completions import com.openai.core.JsonValue 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 +import kotlin.jvm.optionals.getOrNull /** * Delete a stored chat completion. Only Chat Completions that have been created with the `store` @@ -17,13 +17,13 @@ import java.util.Optional */ class ChatCompletionDeleteParams private constructor( - private val completionId: String, + private val completionId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, ) : Params { - fun completionId(): String = completionId + fun completionId(): Optional = Optional.ofNullable(completionId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -35,13 +35,10 @@ private constructor( companion object { + @JvmStatic fun none(): ChatCompletionDeleteParams = builder().build() + /** * Returns a mutable builder for constructing an instance of [ChatCompletionDeleteParams]. - * - * The following fields are required: - * ```java - * .completionId() - * ``` */ @JvmStatic fun builder() = Builder() } @@ -63,7 +60,10 @@ private constructor( chatCompletionDeleteParams.additionalBodyProperties.toMutableMap() } - fun completionId(completionId: String) = apply { this.completionId = completionId } + fun completionId(completionId: String?) = apply { this.completionId = completionId } + + /** Alias for calling [Builder.completionId] with `completionId.orElse(null)`. */ + fun completionId(completionId: Optional) = completionId(completionId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -189,17 +189,10 @@ private constructor( * Returns an immutable instance of [ChatCompletionDeleteParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .completionId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): ChatCompletionDeleteParams = ChatCompletionDeleteParams( - checkRequired("completionId", completionId), + completionId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -211,7 +204,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> completionId + 0 -> completionId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPage.kt index 6c807633..a0c72474 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPage.kt @@ -2,12 +2,12 @@ package com.openai.models.chat.completions +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.chat.ChatCompletionService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [ChatCompletionService.list] */ @@ -16,7 +16,7 @@ private constructor( private val service: ChatCompletionService, private val params: ChatCompletionListParams, private val response: ChatCompletionListPageResponse, -) { +) : Page { /** * Delegates to [ChatCompletionListPageResponse], but gracefully handles missing data. @@ -33,20 +33,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): ChatCompletionListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = - getNextPageParams().map { service.list(it) } + override fun nextPage(): ChatCompletionListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): ChatCompletionListParams = params @@ -115,25 +111,6 @@ private constructor( ) } - class AutoPager(private val firstPage: ChatCompletionListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPageAsync.kt index 87bef9ed..ebd91537 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPageAsync.kt @@ -2,22 +2,24 @@ package com.openai.models.chat.completions +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.chat.ChatCompletionServiceAsync import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [ChatCompletionServiceAsync.list] */ class ChatCompletionListPageAsync private constructor( private val service: ChatCompletionServiceAsync, + private val streamHandlerExecutor: Executor, private val params: ChatCompletionListParams, private val response: ChatCompletionListPageResponse, -) { +) : PageAsync { /** * Delegates to [ChatCompletionListPageResponse], but gracefully handles missing data. @@ -34,22 +36,18 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): ChatCompletionListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = + service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = + AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): ChatCompletionListParams = params @@ -67,6 +65,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -78,18 +77,24 @@ private constructor( class Builder internal constructor() { private var service: ChatCompletionServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: ChatCompletionListParams? = null private var response: ChatCompletionListPageResponse? = null @JvmSynthetic internal fun from(chatCompletionListPageAsync: ChatCompletionListPageAsync) = apply { service = chatCompletionListPageAsync.service + streamHandlerExecutor = chatCompletionListPageAsync.streamHandlerExecutor params = chatCompletionListPageAsync.params response = chatCompletionListPageAsync.response } fun service(service: ChatCompletionServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: ChatCompletionListParams) = apply { this.params = params } @@ -104,6 +109,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -113,50 +119,22 @@ private constructor( fun build(): ChatCompletionListPageAsync = ChatCompletionListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: ChatCompletionListPageAsync) { - - fun forEach( - action: Predicate, - executor: Executor, - ): CompletableFuture { - fun CompletableFuture>.forEach( - action: (ChatCompletion) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is ChatCompletionListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is ChatCompletionListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "ChatCompletionListPageAsync{service=$service, params=$params, response=$response}" + "ChatCompletionListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionRetrieveParams.kt index adf67c89..5254f338 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionRetrieveParams.kt @@ -3,10 +3,11 @@ package com.openai.models.chat.completions import com.openai.core.Params -import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Get a stored chat completion. Only Chat Completions that have been created with the `store` @@ -14,12 +15,12 @@ import java.util.Objects */ class ChatCompletionRetrieveParams private constructor( - private val completionId: String, + private val completionId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun completionId(): String = completionId + fun completionId(): Optional = Optional.ofNullable(completionId) fun _additionalHeaders(): Headers = additionalHeaders @@ -29,13 +30,10 @@ private constructor( companion object { + @JvmStatic fun none(): ChatCompletionRetrieveParams = builder().build() + /** * Returns a mutable builder for constructing an instance of [ChatCompletionRetrieveParams]. - * - * The following fields are required: - * ```java - * .completionId() - * ``` */ @JvmStatic fun builder() = Builder() } @@ -54,7 +52,10 @@ private constructor( additionalQueryParams = chatCompletionRetrieveParams.additionalQueryParams.toBuilder() } - fun completionId(completionId: String) = apply { this.completionId = completionId } + fun completionId(completionId: String?) = apply { this.completionId = completionId } + + /** Alias for calling [Builder.completionId] with `completionId.orElse(null)`. */ + fun completionId(completionId: Optional) = completionId(completionId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -158,17 +159,10 @@ private constructor( * Returns an immutable instance of [ChatCompletionRetrieveParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .completionId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): ChatCompletionRetrieveParams = ChatCompletionRetrieveParams( - checkRequired("completionId", completionId), + completionId, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -176,7 +170,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> completionId + 0 -> completionId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionUpdateParams.kt index 95329885..6468e4e7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionUpdateParams.kt @@ -28,13 +28,13 @@ import kotlin.jvm.optionals.getOrNull */ class ChatCompletionUpdateParams private constructor( - private val completionId: String, + private val completionId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun completionId(): String = completionId + fun completionId(): Optional = Optional.ofNullable(completionId) /** * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing @@ -71,7 +71,6 @@ private constructor( * * The following fields are required: * ```java - * .completionId() * .metadata() * ``` */ @@ -94,7 +93,10 @@ private constructor( additionalQueryParams = chatCompletionUpdateParams.additionalQueryParams.toBuilder() } - fun completionId(completionId: String) = apply { this.completionId = completionId } + fun completionId(completionId: String?) = apply { this.completionId = completionId } + + /** Alias for calling [Builder.completionId] with `completionId.orElse(null)`. */ + fun completionId(completionId: Optional) = completionId(completionId.getOrNull()) /** * Sets the entire request body. @@ -251,7 +253,6 @@ private constructor( * * The following fields are required: * ```java - * .completionId() * .metadata() * ``` * @@ -259,7 +260,7 @@ private constructor( */ fun build(): ChatCompletionUpdateParams = ChatCompletionUpdateParams( - checkRequired("completionId", completionId), + completionId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -270,7 +271,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> completionId + 0 -> completionId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletion.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletion.kt new file mode 100644 index 00000000..872135a4 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletion.kt @@ -0,0 +1,176 @@ +package com.openai.models.chat.completions + +import com.openai.core.JsonField +import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.chat.completions.ChatCompletion.Choice.FinishReason +import com.openai.models.chat.completions.ChatCompletion.Choice.Logprobs +import com.openai.models.chat.completions.ChatCompletion.ServiceTier +import com.openai.models.completions.CompletionUsage +import java.util.Objects +import java.util.Optional + +/** + * A wrapper for [ChatCompletion] that provides type-safe access to the [choices] when using the + * _Structured Outputs_ feature to deserialize a JSON response to an instance of an arbitrary class. + * See the SDK documentation for more details on _Structured Outputs_. + * + * @param T The type of the class to which the JSON data in the response will be deserialized. + */ +class StructuredChatCompletion( + @get:JvmName("responseType") val responseType: Class, + @get:JvmName("rawChatCompletion") val rawChatCompletion: ChatCompletion, +) { + /** @see ChatCompletion.id */ + fun id(): String = rawChatCompletion.id() + + private val choices by lazy { + rawChatCompletion._choices().map { choices -> choices.map { Choice(responseType, it) } } + } + + /** @see ChatCompletion.choices */ + fun choices(): List> = choices.getRequired("choices") + + /** @see ChatCompletion.created */ + fun created(): Long = rawChatCompletion.created() + + /** @see ChatCompletion.model */ + fun model(): String = rawChatCompletion.model() + + /** @see ChatCompletion._object_ */ + fun _object_(): JsonValue = rawChatCompletion._object_() + + /** @see ChatCompletion.serviceTier */ + fun serviceTier(): Optional = rawChatCompletion.serviceTier() + + /** @see ChatCompletion.systemFingerprint */ + fun systemFingerprint(): Optional = rawChatCompletion.systemFingerprint() + + /** @see ChatCompletion.usage */ + fun usage(): Optional = rawChatCompletion.usage() + + /** @see ChatCompletion._id */ + fun _id(): JsonField = rawChatCompletion._id() + + /** @see ChatCompletion._choices */ + fun _choices(): JsonField>> = choices + + /** @see ChatCompletion._created */ + fun _created(): JsonField = rawChatCompletion._created() + + /** @see ChatCompletion._model */ + fun _model(): JsonField = rawChatCompletion._model() + + /** @see ChatCompletion._serviceTier */ + fun _serviceTier(): JsonField = rawChatCompletion._serviceTier() + + /** @see ChatCompletion._systemFingerprint */ + fun _systemFingerprint(): JsonField = rawChatCompletion._systemFingerprint() + + /** @see ChatCompletion._usage */ + fun _usage(): JsonField = rawChatCompletion._usage() + + /** @see ChatCompletion._additionalProperties */ + fun _additionalProperties(): Map = rawChatCompletion._additionalProperties() + + class Choice + internal constructor( + @get:JvmName("responseType") val responseType: Class, + @get:JvmName("rawChoice") val rawChoice: ChatCompletion.Choice, + ) { + /** @see ChatCompletion.Choice.finishReason */ + fun finishReason(): FinishReason = rawChoice.finishReason() + + /** @see ChatCompletion.Choice.index */ + fun index(): Long = rawChoice.index() + + /** @see ChatCompletion.Choice.logprobs */ + fun logprobs(): Optional = rawChoice.logprobs() + + /** @see ChatCompletion.Choice._finishReason */ + fun _finishReason(): JsonField = rawChoice._finishReason() + + private val message by lazy { + rawChoice._message().map { StructuredChatCompletionMessage(responseType, it) } + } + + /** @see ChatCompletion.Choice.message */ + fun message(): StructuredChatCompletionMessage = message.getRequired("message") + + /** @see ChatCompletion.Choice._index */ + fun _index(): JsonField = rawChoice._index() + + /** @see ChatCompletion.Choice._logprobs */ + fun _logprobs(): JsonField = rawChoice._logprobs() + + /** @see ChatCompletion.Choice._message */ + fun _message(): JsonField> = message + + /** @see ChatCompletion.Choice._additionalProperties */ + fun _additionalProperties(): Map = rawChoice._additionalProperties() + + /** @see ChatCompletion.Choice.validate */ + fun validate(): Choice = apply { + message().validate() + rawChoice.validate() + } + + /** @see ChatCompletion.Choice.isValid */ + fun isValid(): Boolean = + try { + validate() + true + } catch (_: OpenAIInvalidDataException) { + false + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Choice<*> && + responseType == other.responseType && + rawChoice == other.rawChoice + } + + private val hashCode: Int by lazy { Objects.hash(responseType, rawChoice) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "${javaClass.simpleName}{responseType=$responseType, rawChoice=$rawChoice}" + } + + /** @see ChatCompletion.validate */ + fun validate() = apply { + choices().forEach { it.validate() } + rawChatCompletion.validate() + } + + /** @see ChatCompletion.isValid */ + fun isValid(): Boolean = + try { + validate() + true + } catch (_: OpenAIInvalidDataException) { + false + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is StructuredChatCompletion<*> && + responseType == other.responseType && + rawChatCompletion == other.rawChatCompletion + } + + private val hashCode: Int by lazy { Objects.hash(responseType, rawChatCompletion) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "${javaClass.simpleName}{responseType=$responseType, rawChatCompletion=$rawChatCompletion}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletionCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletionCreateParams.kt new file mode 100644 index 00000000..73741b58 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletionCreateParams.kt @@ -0,0 +1,765 @@ +package com.openai.models.chat.completions + +import com.openai.core.JsonField +import com.openai.core.JsonSchemaLocalValidation +import com.openai.core.JsonValue +import com.openai.core.checkRequired +import com.openai.core.http.Headers +import com.openai.core.http.QueryParams +import com.openai.core.responseFormatFromClass +import com.openai.models.ChatModel +import com.openai.models.ReasoningEffort +import java.util.Objects +import java.util.Optional + +/** + * A wrapper for [ChatCompletionCreateParams] that provides a type-safe [Builder] that can record + * the [responseType] used to derive a JSON schema from an arbitrary class when using the + * _Structured Outputs_ feature. When a JSON response is received, it is deserialized to am instance + * of that type. See the SDK documentation for more details on _Structured Outputs_. + * + * @param T The type of the class that will be used to derive the JSON schema in the request and to + * which the JSON response will be deserialized. + */ +class StructuredChatCompletionCreateParams +internal constructor( + @get:JvmName("responseType") val responseType: Class, + /** + * The raw, underlying chat completion create parameters wrapped by this structured instance of + * the parameters. + */ + @get:JvmName("rawParams") val rawParams: ChatCompletionCreateParams, +) { + + companion object { + @JvmStatic fun builder() = Builder() + } + + class Builder internal constructor() { + private var responseType: Class? = null + private var paramsBuilder = ChatCompletionCreateParams.builder() + + @JvmSynthetic + internal fun wrap( + responseType: Class, + paramsBuilder: ChatCompletionCreateParams.Builder, + localValidation: JsonSchemaLocalValidation, + ) = apply { + this.responseType = responseType + this.paramsBuilder = paramsBuilder + // Convert the class to a JSON schema and apply it to the delegate `Builder`. + responseFormat(responseType, localValidation) + } + + /** Injects a given `ChatCompletionCreateParams.Builder`. For use only when testing. */ + @JvmSynthetic + internal fun inject(paramsBuilder: ChatCompletionCreateParams.Builder) = apply { + this.paramsBuilder = paramsBuilder + } + + /** @see ChatCompletionCreateParams.Builder.body */ + fun body(body: ChatCompletionCreateParams.Body) = apply { paramsBuilder.body(body) } + + /** @see ChatCompletionCreateParams.Builder.messages */ + fun messages(messages: List) = apply { + paramsBuilder.messages(messages) + } + + /** @see ChatCompletionCreateParams.Builder.messages */ + fun messages(messages: JsonField>) = apply { + paramsBuilder.messages(messages) + } + + /** @see ChatCompletionCreateParams.Builder.addMessage */ + fun addMessage(message: ChatCompletionMessageParam) = apply { + paramsBuilder.addMessage(message) + } + + /** @see ChatCompletionCreateParams.Builder.addMessage */ + fun addMessage(developer: ChatCompletionDeveloperMessageParam) = apply { + paramsBuilder.addMessage(developer) + } + + /** @see ChatCompletionCreateParams.Builder.addDeveloperMessage */ + fun addDeveloperMessage(content: ChatCompletionDeveloperMessageParam.Content) = apply { + paramsBuilder.addDeveloperMessage(content) + } + + /** @see ChatCompletionCreateParams.Builder.addDeveloperMessage */ + fun addDeveloperMessage(text: String) = apply { paramsBuilder.addDeveloperMessage(text) } + + /** @see ChatCompletionCreateParams.Builder.addDeveloperMessageOfArrayOfContentParts */ + fun addDeveloperMessageOfArrayOfContentParts( + arrayOfContentParts: List + ) = apply { paramsBuilder.addDeveloperMessageOfArrayOfContentParts(arrayOfContentParts) } + + /** @see ChatCompletionCreateParams.Builder.addMessage */ + fun addMessage(system: ChatCompletionSystemMessageParam) = apply { + paramsBuilder.addMessage(system) + } + + /** @see ChatCompletionCreateParams.Builder.addSystemMessage */ + fun addSystemMessage(content: ChatCompletionSystemMessageParam.Content) = apply { + paramsBuilder.addSystemMessage(content) + } + + /** @see ChatCompletionCreateParams.Builder.addSystemMessage */ + fun addSystemMessage(text: String) = apply { paramsBuilder.addSystemMessage(text) } + + /** @see ChatCompletionCreateParams.Builder.addSystemMessageOfArrayOfContentParts */ + fun addSystemMessageOfArrayOfContentParts( + arrayOfContentParts: List + ) = apply { paramsBuilder.addSystemMessageOfArrayOfContentParts(arrayOfContentParts) } + + /** @see ChatCompletionCreateParams.Builder.addMessage */ + fun addMessage(user: ChatCompletionUserMessageParam) = apply { + paramsBuilder.addMessage(user) + } + + /** @see ChatCompletionCreateParams.Builder.addUserMessage */ + fun addUserMessage(content: ChatCompletionUserMessageParam.Content) = apply { + paramsBuilder.addUserMessage(content) + } + + /** @see ChatCompletionCreateParams.Builder.addUserMessage */ + fun addUserMessage(text: String) = apply { paramsBuilder.addUserMessage(text) } + + /** @see ChatCompletionCreateParams.Builder.addUserMessageOfArrayOfContentParts */ + fun addUserMessageOfArrayOfContentParts( + arrayOfContentParts: List + ) = apply { paramsBuilder.addUserMessageOfArrayOfContentParts(arrayOfContentParts) } + + /** @see ChatCompletionCreateParams.Builder.addMessage */ + fun addMessage(assistant: ChatCompletionAssistantMessageParam) = apply { + paramsBuilder.addMessage(assistant) + } + + /** @see ChatCompletionCreateParams.Builder.addMessage */ + fun addMessage(assistant: ChatCompletionMessage) = apply { + paramsBuilder.addMessage(assistant) + } + + /** @see ChatCompletionCreateParams.Builder.addMessage */ + fun addMessage(tool: ChatCompletionToolMessageParam) = apply { + paramsBuilder.addMessage(tool) + } + + /** @see ChatCompletionCreateParams.Builder.addMessage */ + @Deprecated("deprecated") + fun addMessage(function: ChatCompletionFunctionMessageParam) = apply { + paramsBuilder.addMessage(function) + } + + /** @see ChatCompletionCreateParams.Builder.model */ + fun model(model: ChatModel) = apply { paramsBuilder.model(model) } + + /** @see ChatCompletionCreateParams.Builder.model */ + fun model(model: JsonField) = apply { paramsBuilder.model(model) } + + /** @see ChatCompletionCreateParams.Builder.model */ + fun model(value: String) = apply { paramsBuilder.model(value) } + + /** @see ChatCompletionCreateParams.Builder.audio */ + fun audio(audio: ChatCompletionAudioParam?) = apply { paramsBuilder.audio(audio) } + + /** @see ChatCompletionCreateParams.Builder.audio */ + fun audio(audio: Optional) = apply { paramsBuilder.audio(audio) } + + /** @see ChatCompletionCreateParams.Builder.audio */ + fun audio(audio: JsonField) = apply { paramsBuilder.audio(audio) } + + /** @see ChatCompletionCreateParams.Builder.frequencyPenalty */ + fun frequencyPenalty(frequencyPenalty: Double?) = apply { + paramsBuilder.frequencyPenalty(frequencyPenalty) + } + + /** @see ChatCompletionCreateParams.Builder.frequencyPenalty */ + fun frequencyPenalty(frequencyPenalty: Double) = apply { + paramsBuilder.frequencyPenalty(frequencyPenalty) + } + + /** @see ChatCompletionCreateParams.Builder.frequencyPenalty */ + fun frequencyPenalty(frequencyPenalty: Optional) = apply { + paramsBuilder.frequencyPenalty(frequencyPenalty) + } + + /** @see ChatCompletionCreateParams.Builder.frequencyPenalty */ + fun frequencyPenalty(frequencyPenalty: JsonField) = apply { + paramsBuilder.frequencyPenalty(frequencyPenalty) + } + + /** @see ChatCompletionCreateParams.Builder.functionCall */ + @Deprecated("deprecated") + fun functionCall(functionCall: ChatCompletionCreateParams.FunctionCall) = apply { + paramsBuilder.functionCall(functionCall) + } + + /** @see ChatCompletionCreateParams.Builder.functionCall */ + @Deprecated("deprecated") + fun functionCall(functionCall: JsonField) = apply { + paramsBuilder.functionCall(functionCall) + } + + /** @see ChatCompletionCreateParams.Builder.functionCall */ + @Deprecated("deprecated") + fun functionCall(mode: ChatCompletionCreateParams.FunctionCall.FunctionCallMode) = apply { + paramsBuilder.functionCall(mode) + } + + /** @see ChatCompletionCreateParams.Builder.functionCall */ + @Deprecated("deprecated") + fun functionCall(functionCallOption: ChatCompletionFunctionCallOption) = apply { + paramsBuilder.functionCall(functionCallOption) + } + + /** @see ChatCompletionCreateParams.Builder.functions */ + @Deprecated("deprecated") + fun functions(functions: List) = apply { + paramsBuilder.functions(functions) + } + + /** @see ChatCompletionCreateParams.Builder.functions */ + @Deprecated("deprecated") + fun functions(functions: JsonField>) = apply { + paramsBuilder.functions(functions) + } + + /** @see ChatCompletionCreateParams.Builder.addFunction */ + @Deprecated("deprecated") + fun addFunction(function: ChatCompletionCreateParams.Function) = apply { + paramsBuilder.addFunction(function) + } + + /** @see ChatCompletionCreateParams.Builder.logitBias */ + fun logitBias(logitBias: ChatCompletionCreateParams.LogitBias?) = apply { + paramsBuilder.logitBias(logitBias) + } + + /** @see ChatCompletionCreateParams.Builder.logitBias */ + fun logitBias(logitBias: Optional) = apply { + paramsBuilder.logitBias(logitBias) + } + + /** @see ChatCompletionCreateParams.Builder.logitBias */ + fun logitBias(logitBias: JsonField) = apply { + paramsBuilder.logitBias(logitBias) + } + + /** @see ChatCompletionCreateParams.Builder.logprobs */ + fun logprobs(logprobs: Boolean?) = apply { paramsBuilder.logprobs(logprobs) } + + /** @see ChatCompletionCreateParams.Builder.logprobs */ + fun logprobs(logprobs: Boolean) = apply { paramsBuilder.logprobs(logprobs) } + + /** @see ChatCompletionCreateParams.Builder.logprobs */ + fun logprobs(logprobs: Optional) = apply { paramsBuilder.logprobs(logprobs) } + + /** @see ChatCompletionCreateParams.Builder.logprobs */ + fun logprobs(logprobs: JsonField) = apply { paramsBuilder.logprobs(logprobs) } + + /** @see ChatCompletionCreateParams.Builder.maxCompletionTokens */ + fun maxCompletionTokens(maxCompletionTokens: Long?) = apply { + paramsBuilder.maxCompletionTokens(maxCompletionTokens) + } + + /** @see ChatCompletionCreateParams.Builder.maxCompletionTokens */ + fun maxCompletionTokens(maxCompletionTokens: Long) = apply { + paramsBuilder.maxCompletionTokens(maxCompletionTokens) + } + + /** @see ChatCompletionCreateParams.Builder.maxCompletionTokens */ + fun maxCompletionTokens(maxCompletionTokens: Optional) = apply { + paramsBuilder.maxCompletionTokens(maxCompletionTokens) + } + + /** @see ChatCompletionCreateParams.Builder.maxCompletionTokens */ + fun maxCompletionTokens(maxCompletionTokens: JsonField) = apply { + paramsBuilder.maxCompletionTokens(maxCompletionTokens) + } + + /** @see ChatCompletionCreateParams.Builder.maxTokens */ + @Deprecated("deprecated") + fun maxTokens(maxTokens: Long?) = apply { paramsBuilder.maxTokens(maxTokens) } + + /** @see ChatCompletionCreateParams.Builder.maxTokens */ + @Deprecated("deprecated") + fun maxTokens(maxTokens: Long) = apply { paramsBuilder.maxTokens(maxTokens) } + + /** @see ChatCompletionCreateParams.Builder.maxTokens */ + @Deprecated("deprecated") + fun maxTokens(maxTokens: Optional) = apply { paramsBuilder.maxTokens(maxTokens) } + + /** @see ChatCompletionCreateParams.Builder.maxTokens */ + @Deprecated("deprecated") + fun maxTokens(maxTokens: JsonField) = apply { paramsBuilder.maxTokens(maxTokens) } + + /** @see ChatCompletionCreateParams.Builder.metadata */ + fun metadata(metadata: ChatCompletionCreateParams.Metadata?) = apply { + paramsBuilder.metadata(metadata) + } + + /** @see ChatCompletionCreateParams.Builder.metadata */ + fun metadata(metadata: Optional) = apply { + paramsBuilder.metadata(metadata) + } + + /** @see ChatCompletionCreateParams.Builder.metadata */ + fun metadata(metadata: JsonField) = apply { + paramsBuilder.metadata(metadata) + } + + /** @see ChatCompletionCreateParams.Builder.modalities */ + fun modalities(modalities: List?) = apply { + paramsBuilder.modalities(modalities) + } + + /** @see ChatCompletionCreateParams.Builder.modalities */ + fun modalities(modalities: Optional>) = apply { + paramsBuilder.modalities(modalities) + } + + /** @see ChatCompletionCreateParams.Builder.modalities */ + fun modalities(modalities: JsonField>) = apply { + paramsBuilder.modalities(modalities) + } + + /** @see ChatCompletionCreateParams.Builder.addModality */ + fun addModality(modality: ChatCompletionCreateParams.Modality) = apply { + paramsBuilder.addModality(modality) + } + + /** @see ChatCompletionCreateParams.Builder.n */ + fun n(n: Long?) = apply { paramsBuilder.n(n) } + + /** @see ChatCompletionCreateParams.Builder.n */ + fun n(n: Long) = apply { paramsBuilder.n(n) } + + /** @see ChatCompletionCreateParams.Builder.n */ + fun n(n: Optional) = apply { paramsBuilder.n(n) } + + /** @see ChatCompletionCreateParams.Builder.n */ + fun n(n: JsonField) = apply { paramsBuilder.n(n) } + + /** @see ChatCompletionCreateParams.Builder.parallelToolCalls */ + fun parallelToolCalls(parallelToolCalls: Boolean) = apply { + paramsBuilder.parallelToolCalls(parallelToolCalls) + } + + /** @see ChatCompletionCreateParams.Builder.parallelToolCalls */ + fun parallelToolCalls(parallelToolCalls: JsonField) = apply { + paramsBuilder.parallelToolCalls(parallelToolCalls) + } + + /** @see ChatCompletionCreateParams.Builder.prediction */ + fun prediction(prediction: ChatCompletionPredictionContent?) = apply { + paramsBuilder.prediction(prediction) + } + + /** @see ChatCompletionCreateParams.Builder.prediction */ + fun prediction(prediction: Optional) = apply { + paramsBuilder.prediction(prediction) + } + + /** @see ChatCompletionCreateParams.Builder.prediction */ + fun prediction(prediction: JsonField) = apply { + paramsBuilder.prediction(prediction) + } + + /** @see ChatCompletionCreateParams.Builder.presencePenalty */ + fun presencePenalty(presencePenalty: Double?) = apply { + paramsBuilder.presencePenalty(presencePenalty) + } + + /** @see ChatCompletionCreateParams.Builder.presencePenalty */ + fun presencePenalty(presencePenalty: Double) = apply { + paramsBuilder.presencePenalty(presencePenalty) + } + + /** @see ChatCompletionCreateParams.Builder.presencePenalty */ + fun presencePenalty(presencePenalty: Optional) = apply { + paramsBuilder.presencePenalty(presencePenalty) + } + + /** @see ChatCompletionCreateParams.Builder.presencePenalty */ + fun presencePenalty(presencePenalty: JsonField) = apply { + paramsBuilder.presencePenalty(presencePenalty) + } + + /** @see ChatCompletionCreateParams.Builder.reasoningEffort */ + fun reasoningEffort(reasoningEffort: ReasoningEffort?) = apply { + paramsBuilder.reasoningEffort(reasoningEffort) + } + + /** @see ChatCompletionCreateParams.Builder.reasoningEffort */ + fun reasoningEffort(reasoningEffort: Optional) = apply { + paramsBuilder.reasoningEffort(reasoningEffort) + } + + /** @see ChatCompletionCreateParams.Builder.reasoningEffort */ + fun reasoningEffort(reasoningEffort: JsonField) = apply { + paramsBuilder.reasoningEffort(reasoningEffort) + } + + /** + * Sets the response format to a JSON schema derived from the structure of the given class. + * + * @see ChatCompletionCreateParams.Builder.responseFormat + */ + @JvmOverloads + fun responseFormat( + responseType: Class, + localValidation: JsonSchemaLocalValidation = JsonSchemaLocalValidation.YES, + ) = apply { + this.responseType = responseType + paramsBuilder.responseFormat(responseFormatFromClass(responseType, localValidation)) + } + + /** @see ChatCompletionCreateParams.Builder.seed */ + fun seed(seed: Long?) = apply { paramsBuilder.seed(seed) } + + /** @see ChatCompletionCreateParams.Builder.seed */ + fun seed(seed: Long) = apply { paramsBuilder.seed(seed) } + + /** @see ChatCompletionCreateParams.Builder.seed */ + fun seed(seed: Optional) = apply { paramsBuilder.seed(seed) } + + /** @see ChatCompletionCreateParams.Builder.seed */ + fun seed(seed: JsonField) = apply { paramsBuilder.seed(seed) } + + /** @see ChatCompletionCreateParams.Builder.serviceTier */ + fun serviceTier(serviceTier: ChatCompletionCreateParams.ServiceTier?) = apply { + paramsBuilder.serviceTier(serviceTier) + } + + /** @see ChatCompletionCreateParams.Builder.serviceTier */ + fun serviceTier(serviceTier: Optional) = apply { + paramsBuilder.serviceTier(serviceTier) + } + + /** @see ChatCompletionCreateParams.Builder.serviceTier */ + fun serviceTier(serviceTier: JsonField) = apply { + paramsBuilder.serviceTier(serviceTier) + } + + /** @see ChatCompletionCreateParams.Builder.stop */ + fun stop(stop: ChatCompletionCreateParams.Stop?) = apply { paramsBuilder.stop(stop) } + + /** @see ChatCompletionCreateParams.Builder.stop */ + fun stop(stop: Optional) = apply { + paramsBuilder.stop(stop) + } + + /** @see ChatCompletionCreateParams.Builder.stop */ + fun stop(stop: JsonField) = apply { + paramsBuilder.stop(stop) + } + + /** @see ChatCompletionCreateParams.Builder.stop */ + fun stop(string: String) = apply { paramsBuilder.stop(string) } + + /** @see ChatCompletionCreateParams.Builder.stopOfStrings */ + fun stopOfStrings(strings: List) = apply { paramsBuilder.stopOfStrings(strings) } + + /** @see ChatCompletionCreateParams.Builder.store */ + fun store(store: Boolean?) = apply { paramsBuilder.store(store) } + + /** @see ChatCompletionCreateParams.Builder.store */ + fun store(store: Boolean) = apply { paramsBuilder.store(store) } + + /** @see ChatCompletionCreateParams.Builder.store */ + fun store(store: Optional) = apply { paramsBuilder.store(store) } + + /** @see ChatCompletionCreateParams.Builder.store */ + fun store(store: JsonField) = apply { paramsBuilder.store(store) } + + /** @see ChatCompletionCreateParams.Builder.streamOptions */ + fun streamOptions(streamOptions: ChatCompletionStreamOptions?) = apply { + paramsBuilder.streamOptions(streamOptions) + } + + /** @see ChatCompletionCreateParams.Builder.streamOptions */ + fun streamOptions(streamOptions: Optional) = apply { + paramsBuilder.streamOptions(streamOptions) + } + + /** @see ChatCompletionCreateParams.Builder.streamOptions */ + fun streamOptions(streamOptions: JsonField) = apply { + paramsBuilder.streamOptions(streamOptions) + } + + /** @see ChatCompletionCreateParams.Builder.temperature */ + fun temperature(temperature: Double?) = apply { paramsBuilder.temperature(temperature) } + + /** @see ChatCompletionCreateParams.Builder.temperature */ + fun temperature(temperature: Double) = apply { paramsBuilder.temperature(temperature) } + + /** @see ChatCompletionCreateParams.Builder.temperature */ + fun temperature(temperature: Optional) = apply { + paramsBuilder.temperature(temperature) + } + + /** @see ChatCompletionCreateParams.Builder.temperature */ + fun temperature(temperature: JsonField) = apply { + paramsBuilder.temperature(temperature) + } + + /** @see ChatCompletionCreateParams.Builder.toolChoice */ + fun toolChoice(toolChoice: ChatCompletionToolChoiceOption) = apply { + paramsBuilder.toolChoice(toolChoice) + } + + /** @see ChatCompletionCreateParams.Builder.toolChoice */ + fun toolChoice(toolChoice: JsonField) = apply { + paramsBuilder.toolChoice(toolChoice) + } + + /** @see ChatCompletionCreateParams.Builder.toolChoice */ + fun toolChoice(auto: ChatCompletionToolChoiceOption.Auto) = apply { + paramsBuilder.toolChoice(auto) + } + + /** @see ChatCompletionCreateParams.Builder.toolChoice */ + fun toolChoice(namedToolChoice: ChatCompletionNamedToolChoice) = apply { + paramsBuilder.toolChoice(namedToolChoice) + } + + /** @see ChatCompletionCreateParams.Builder.tools */ + fun tools(tools: List) = apply { paramsBuilder.tools(tools) } + + /** @see ChatCompletionCreateParams.Builder.tools */ + fun tools(tools: JsonField>) = apply { paramsBuilder.tools(tools) } + + /** @see ChatCompletionCreateParams.Builder.addTool */ + fun addTool(tool: ChatCompletionTool) = apply { paramsBuilder.addTool(tool) } + + /** @see ChatCompletionCreateParams.Builder.topLogprobs */ + fun topLogprobs(topLogprobs: Long?) = apply { paramsBuilder.topLogprobs(topLogprobs) } + + /** @see ChatCompletionCreateParams.Builder.topLogprobs */ + fun topLogprobs(topLogprobs: Long) = apply { paramsBuilder.topLogprobs(topLogprobs) } + + /** @see ChatCompletionCreateParams.Builder.topLogprobs */ + fun topLogprobs(topLogprobs: Optional) = apply { + paramsBuilder.topLogprobs(topLogprobs) + } + + /** @see ChatCompletionCreateParams.Builder.topLogprobs */ + fun topLogprobs(topLogprobs: JsonField) = apply { + paramsBuilder.topLogprobs(topLogprobs) + } + + /** @see ChatCompletionCreateParams.Builder.topP */ + fun topP(topP: Double?) = apply { paramsBuilder.topP(topP) } + + /** @see ChatCompletionCreateParams.Builder.topP */ + fun topP(topP: Double) = apply { paramsBuilder.topP(topP) } + + /** @see ChatCompletionCreateParams.Builder.topP */ + fun topP(topP: Optional) = apply { paramsBuilder.topP(topP) } + + /** @see ChatCompletionCreateParams.Builder.topP */ + fun topP(topP: JsonField) = apply { paramsBuilder.topP(topP) } + + /** @see ChatCompletionCreateParams.Builder.user */ + fun user(user: String) = apply { paramsBuilder.user(user) } + + /** @see ChatCompletionCreateParams.Builder.user */ + fun user(user: JsonField) = apply { paramsBuilder.user(user) } + + /** @see ChatCompletionCreateParams.Builder.webSearchOptions */ + fun webSearchOptions(webSearchOptions: ChatCompletionCreateParams.WebSearchOptions) = + apply { + paramsBuilder.webSearchOptions(webSearchOptions) + } + + /** @see ChatCompletionCreateParams.Builder.webSearchOptions */ + fun webSearchOptions( + webSearchOptions: JsonField + ) = apply { paramsBuilder.webSearchOptions(webSearchOptions) } + + /** @see ChatCompletionCreateParams.Builder.additionalBodyProperties */ + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + paramsBuilder.additionalBodyProperties(additionalBodyProperties) + } + + /** @see ChatCompletionCreateParams.Builder.putAdditionalBodyProperty */ + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + paramsBuilder.putAdditionalBodyProperty(key, value) + } + + /** @see ChatCompletionCreateParams.Builder.putAllAdditionalBodyProperties */ + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + paramsBuilder.putAllAdditionalBodyProperties(additionalBodyProperties) + } + + /** @see ChatCompletionCreateParams.Builder.removeAdditionalBodyProperty */ + fun removeAdditionalBodyProperty(key: String) = apply { + paramsBuilder.removeAdditionalBodyProperty(key) + } + + /** @see ChatCompletionCreateParams.Builder.removeAllAdditionalBodyProperties */ + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + paramsBuilder.removeAllAdditionalBodyProperties(keys) + } + + /** @see ChatCompletionCreateParams.Builder.additionalHeaders */ + fun additionalHeaders(additionalHeaders: Headers) = apply { + paramsBuilder.additionalHeaders(additionalHeaders) + } + + /** @see ChatCompletionCreateParams.Builder.additionalHeaders */ + fun additionalHeaders(additionalHeaders: Map>) = apply { + paramsBuilder.additionalHeaders(additionalHeaders) + } + + /** @see ChatCompletionCreateParams.Builder.putAdditionalHeader */ + fun putAdditionalHeader(name: String, value: String) = apply { + paramsBuilder.putAdditionalHeader(name, value) + } + + /** @see ChatCompletionCreateParams.Builder.putAdditionalHeaders */ + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + paramsBuilder.putAdditionalHeaders(name, values) + } + + /** @see ChatCompletionCreateParams.Builder.putAllAdditionalHeaders */ + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + paramsBuilder.putAllAdditionalHeaders(additionalHeaders) + } + + /** @see ChatCompletionCreateParams.Builder.putAllAdditionalHeaders */ + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + paramsBuilder.putAllAdditionalHeaders(additionalHeaders) + } + + /** @see ChatCompletionCreateParams.Builder.replaceAdditionalHeaders */ + fun replaceAdditionalHeaders(name: String, value: String) = apply { + paramsBuilder.replaceAdditionalHeaders(name, value) + } + + /** @see ChatCompletionCreateParams.Builder.replaceAdditionalHeaders */ + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + paramsBuilder.replaceAdditionalHeaders(name, values) + } + + /** @see ChatCompletionCreateParams.Builder.replaceAllAdditionalHeaders */ + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + paramsBuilder.replaceAllAdditionalHeaders(additionalHeaders) + } + + /** @see ChatCompletionCreateParams.Builder.replaceAllAdditionalHeaders */ + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + paramsBuilder.replaceAllAdditionalHeaders(additionalHeaders) + } + + /** @see ChatCompletionCreateParams.Builder.removeAdditionalHeaders */ + fun removeAdditionalHeaders(name: String) = apply { + paramsBuilder.removeAdditionalHeaders(name) + } + + /** @see ChatCompletionCreateParams.Builder.removeAllAdditionalHeaders */ + fun removeAllAdditionalHeaders(names: Set) = apply { + paramsBuilder.removeAllAdditionalHeaders(names) + } + + /** @see ChatCompletionCreateParams.Builder.additionalQueryParams */ + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + paramsBuilder.additionalQueryParams(additionalQueryParams) + } + + /** @see ChatCompletionCreateParams.Builder.additionalQueryParams */ + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + paramsBuilder.additionalQueryParams(additionalQueryParams) + } + + /** @see ChatCompletionCreateParams.Builder.putAdditionalQueryParam */ + fun putAdditionalQueryParam(key: String, value: String) = apply { + paramsBuilder.putAdditionalQueryParam(key, value) + } + + /** @see ChatCompletionCreateParams.Builder.putAdditionalQueryParams */ + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + paramsBuilder.putAdditionalQueryParams(key, values) + } + + /** @see ChatCompletionCreateParams.Builder.putAllAdditionalQueryParams */ + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + paramsBuilder.putAllAdditionalQueryParams(additionalQueryParams) + } + + /** @see ChatCompletionCreateParams.Builder.putAllAdditionalQueryParams */ + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + paramsBuilder.putAllAdditionalQueryParams(additionalQueryParams) + } + + /** @see ChatCompletionCreateParams.Builder.replaceAdditionalQueryParams */ + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + paramsBuilder.replaceAdditionalQueryParams(key, value) + } + + /** @see ChatCompletionCreateParams.Builder.replaceAdditionalQueryParams */ + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + paramsBuilder.replaceAdditionalQueryParams(key, values) + } + + /** @see ChatCompletionCreateParams.Builder.replaceAllAdditionalQueryParams */ + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + paramsBuilder.replaceAllAdditionalQueryParams(additionalQueryParams) + } + + /** @see ChatCompletionCreateParams.Builder.replaceAllAdditionalQueryParams */ + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + paramsBuilder.replaceAllAdditionalQueryParams(additionalQueryParams) + } + + /** @see ChatCompletionCreateParams.Builder.removeAdditionalQueryParams */ + fun removeAdditionalQueryParams(key: String) = apply { + paramsBuilder.removeAdditionalQueryParams(key) + } + + /** @see ChatCompletionCreateParams.Builder.removeAllAdditionalQueryParams */ + fun removeAllAdditionalQueryParams(keys: Set) = apply { + paramsBuilder.removeAllAdditionalQueryParams(keys) + } + + /** + * Returns an immutable instance of [StructuredChatCompletionCreateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .messages() + * .model() + * .responseFormat() + * ``` + * + * @throws IllegalStateException If any required field is unset. + */ + fun build() = + StructuredChatCompletionCreateParams( + checkRequired("responseType", responseType), + paramsBuilder.build(), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is StructuredChatCompletionCreateParams<*> && + responseType == other.responseType && + rawParams == other.rawParams + } + + private val hashCode: Int by lazy { Objects.hash(responseType, rawParams) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "${javaClass.simpleName}{responseType=$responseType, rawParams=$rawParams}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletionMessage.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletionMessage.kt new file mode 100644 index 00000000..27e8a3c7 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletionMessage.kt @@ -0,0 +1,95 @@ +package com.openai.models.chat.completions + +import com.openai.core.JsonField +import com.openai.core.JsonValue +import com.openai.core.responseTypeFromJson +import com.openai.models.chat.completions.ChatCompletionMessage.FunctionCall +import java.util.Objects +import java.util.Optional + +/** + * A wrapper for [ChatCompletionMessage] that provides type-safe access to the [content] when using + * the _Structured Outputs_ feature to deserialize a JSON response to an instance of an arbitrary + * class. See the SDK documentation for more details on _Structured Outputs_. + * + * @param T The type of the class to which the JSON data in the content will be deserialized when + * [content] is called. + */ +class StructuredChatCompletionMessage +internal constructor( + @get:JvmName("responseType") val responseType: Class, + @get:JvmName("rawMessage") val rawMessage: ChatCompletionMessage, +) { + + private val content: JsonField by lazy { + rawMessage._content().map { responseTypeFromJson(it, responseType) } + } + + /** @see ChatCompletionMessage.content */ + fun content(): Optional = content.getOptional("content") + + /** @see ChatCompletionMessage.refusal */ + fun refusal(): Optional = rawMessage.refusal() + + /** @see ChatCompletionMessage._role */ + fun _role(): JsonValue = rawMessage._role() + + /** @see ChatCompletionMessage.annotations */ + fun annotations(): Optional> = rawMessage.annotations() + + /** @see ChatCompletionMessage.audio */ + fun audio(): Optional = rawMessage.audio() + + /** @see ChatCompletionMessage.functionCall */ + @Deprecated("deprecated") fun functionCall(): Optional = rawMessage.functionCall() + + /** @see ChatCompletionMessage.toolCalls */ + fun toolCalls(): Optional> = rawMessage.toolCalls() + + /** @see ChatCompletionMessage._content */ + fun _content(): JsonField = content + + /** @see ChatCompletionMessage._refusal */ + fun _refusal(): JsonField = rawMessage._refusal() + + /** @see ChatCompletionMessage._annotations */ + fun _annotations(): JsonField> = + rawMessage._annotations() + + /** @see ChatCompletionMessage._audio */ + fun _audio(): JsonField = rawMessage._audio() + + /** @see ChatCompletionMessage._functionCall */ + @Deprecated("deprecated") + fun _functionCall(): JsonField = rawMessage._functionCall() + + /** @see ChatCompletionMessage._toolCalls */ + fun _toolCalls(): JsonField> = rawMessage._toolCalls() + + /** @see ChatCompletionMessage._additionalProperties */ + fun _additionalProperties(): Map = rawMessage._additionalProperties() + + /** @see ChatCompletionMessage.validate */ + // `content()` is not included in the validation by the delegate method, so just call it. + fun validate(): ChatCompletionMessage = rawMessage.validate() + + /** @see ChatCompletionMessage.isValid */ + fun isValid(): Boolean = rawMessage.isValid() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is StructuredChatCompletionMessage<*> && + responseType == other.responseType && + rawMessage == other.rawMessage + } + + private val hashCode: Int by lazy { Objects.hash(responseType, rawMessage) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "${javaClass.simpleName}{responseType=$responseType, rawMessage=$rawMessage}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPage.kt index 4088b3ef..bc6f6aa6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPage.kt @@ -2,13 +2,13 @@ package com.openai.models.chat.completions.messages +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.models.chat.completions.ChatCompletionStoreMessage import com.openai.services.blocking.chat.completions.MessageService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [MessageService.list] */ @@ -17,7 +17,7 @@ private constructor( private val service: MessageService, private val params: MessageListParams, private val response: MessageListPageResponse, -) { +) : Page { /** * Delegates to [MessageListPageResponse], but gracefully handles missing data. @@ -34,19 +34,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): MessageListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + override fun nextPage(): MessageListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): MessageListParams = params @@ -115,25 +112,6 @@ private constructor( ) } - class AutoPager(private val firstPage: MessageListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPageAsync.kt index d013eff2..7baa95a6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPageAsync.kt @@ -2,6 +2,8 @@ package com.openai.models.chat.completions.messages +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.models.chat.completions.ChatCompletionStoreMessage import com.openai.services.async.chat.completions.MessageServiceAsync @@ -9,16 +11,16 @@ import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [MessageServiceAsync.list] */ class MessageListPageAsync private constructor( private val service: MessageServiceAsync, + private val streamHandlerExecutor: Executor, private val params: MessageListParams, private val response: MessageListPageResponse, -) { +) : PageAsync { /** * Delegates to [MessageListPageResponse], but gracefully handles missing data. @@ -35,22 +37,18 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): MessageListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = + service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = + AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): MessageListParams = params @@ -68,6 +66,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -79,18 +78,24 @@ private constructor( class Builder internal constructor() { private var service: MessageServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: MessageListParams? = null private var response: MessageListPageResponse? = null @JvmSynthetic internal fun from(messageListPageAsync: MessageListPageAsync) = apply { service = messageListPageAsync.service + streamHandlerExecutor = messageListPageAsync.streamHandlerExecutor params = messageListPageAsync.params response = messageListPageAsync.response } fun service(service: MessageServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: MessageListParams) = apply { this.params = params } @@ -105,6 +110,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -114,50 +120,22 @@ private constructor( fun build(): MessageListPageAsync = MessageListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: MessageListPageAsync) { - - fun forEach( - action: Predicate, - executor: Executor, - ): CompletableFuture { - fun CompletableFuture>.forEach( - action: (ChatCompletionStoreMessage) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is MessageListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is MessageListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "MessageListPageAsync{service=$service, params=$params, response=$response}" + "MessageListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListParams.kt index 0f7c3f25..a60bc6d6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListParams.kt @@ -6,7 +6,6 @@ import com.fasterxml.jackson.annotation.JsonCreator import com.openai.core.Enum import com.openai.core.JsonField 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.errors.OpenAIInvalidDataException @@ -20,7 +19,7 @@ import kotlin.jvm.optionals.getOrNull */ class MessageListParams private constructor( - private val completionId: String, + private val completionId: String?, private val after: String?, private val limit: Long?, private val order: Order?, @@ -28,7 +27,7 @@ private constructor( private val additionalQueryParams: QueryParams, ) : Params { - fun completionId(): String = completionId + fun completionId(): Optional = Optional.ofNullable(completionId) /** Identifier for the last message from the previous pagination request. */ fun after(): Optional = Optional.ofNullable(after) @@ -50,14 +49,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [MessageListParams]. - * - * The following fields are required: - * ```java - * .completionId() - * ``` - */ + @JvmStatic fun none(): MessageListParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [MessageListParams]. */ @JvmStatic fun builder() = Builder() } @@ -81,7 +75,10 @@ private constructor( additionalQueryParams = messageListParams.additionalQueryParams.toBuilder() } - fun completionId(completionId: String) = apply { this.completionId = completionId } + fun completionId(completionId: String?) = apply { this.completionId = completionId } + + /** Alias for calling [Builder.completionId] with `completionId.orElse(null)`. */ + fun completionId(completionId: Optional) = completionId(completionId.getOrNull()) /** Identifier for the last message from the previous pagination request. */ fun after(after: String?) = apply { this.after = after } @@ -213,17 +210,10 @@ private constructor( * Returns an immutable instance of [MessageListParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .completionId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): MessageListParams = MessageListParams( - checkRequired("completionId", completionId), + completionId, after, limit, order, @@ -234,7 +224,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> completionId + 0 -> completionId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/embeddings/EmbeddingCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/embeddings/EmbeddingCreateParams.kt index bece5734..6ea30e45 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/embeddings/EmbeddingCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/embeddings/EmbeddingCreateParams.kt @@ -43,11 +43,11 @@ private constructor( /** * Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in a * single request, pass an array of strings or array of token arrays. The input must not exceed - * the max input tokens for the model (8192 tokens for `text-embedding-ada-002`), cannot be an - * empty string, and any array must be 2048 dimensions or less. + * the max input tokens for the model (8192 tokens for all embedding models), cannot be an empty + * string, and any array must be 2048 dimensions or less. * [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) - * for counting tokens. Some models may also impose a limit on total number of tokens summed - * across inputs. + * for counting tokens. In addition to the per-input token limit, all embedding models enforce a + * maximum of 300,000 tokens summed across all inputs in a single request. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -181,11 +181,11 @@ private constructor( /** * Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in * a single request, pass an array of strings or array of token arrays. The input must not - * exceed the max input tokens for the model (8192 tokens for `text-embedding-ada-002`), - * cannot be an empty string, and any array must be 2048 dimensions or less. + * exceed the max input tokens for the model (8192 tokens for all embedding models), cannot + * be an empty string, and any array must be 2048 dimensions or less. * [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) - * for counting tokens. Some models may also impose a limit on total number of tokens summed - * across inputs. + * for counting tokens. In addition to the per-input token limit, all embedding models + * enforce a maximum of 300,000 tokens summed across all inputs in a single request. */ fun input(input: Input) = apply { body.input(input) } @@ -460,11 +460,11 @@ private constructor( /** * Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in * a single request, pass an array of strings or array of token arrays. The input must not - * exceed the max input tokens for the model (8192 tokens for `text-embedding-ada-002`), - * cannot be an empty string, and any array must be 2048 dimensions or less. + * exceed the max input tokens for the model (8192 tokens for all embedding models), cannot + * be an empty string, and any array must be 2048 dimensions or less. * [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) - * for counting tokens. Some models may also impose a limit on total number of tokens summed - * across inputs. + * for counting tokens. In addition to the per-input token limit, all embedding models + * enforce a maximum of 300,000 tokens summed across all inputs in a single request. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -598,12 +598,11 @@ private constructor( /** * Input text to embed, encoded as a string or array of tokens. To embed multiple inputs * in a single request, pass an array of strings or array of token arrays. The input - * must not exceed the max input tokens for the model (8192 tokens for - * `text-embedding-ada-002`), cannot be an empty string, and any array must be 2048 - * dimensions or less. + * must not exceed the max input tokens for the model (8192 tokens for all embedding + * models), cannot be an empty string, and any array must be 2048 dimensions or less. * [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) - * for counting tokens. Some models may also impose a limit on total number of tokens - * summed across inputs. + * for counting tokens. In addition to the per-input token limit, all embedding models + * enforce a maximum of 300,000 tokens summed across all inputs in a single request. */ fun input(input: Input) = input(JsonField.of(input)) @@ -807,11 +806,11 @@ private constructor( /** * Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in a * single request, pass an array of strings or array of token arrays. The input must not exceed - * the max input tokens for the model (8192 tokens for `text-embedding-ada-002`), cannot be an - * empty string, and any array must be 2048 dimensions or less. + * the max input tokens for the model (8192 tokens for all embedding models), cannot be an empty + * string, and any array must be 2048 dimensions or less. * [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) - * for counting tokens. Some models may also impose a limit on total number of tokens summed - * across inputs. + * for counting tokens. In addition to the per-input token limit, all embedding models enforce a + * maximum of 300,000 tokens summed across all inputs in a single request. */ @JsonDeserialize(using = Input.Deserializer::class) @JsonSerialize(using = Input.Serializer::class) diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalCreateParams.kt index 08525daa..88046a7f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalCreateParams.kt @@ -29,6 +29,10 @@ import com.openai.core.http.Headers import com.openai.core.http.QueryParams import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.graders.gradermodels.PythonGrader +import com.openai.models.graders.gradermodels.ScoreModelGrader +import com.openai.models.graders.gradermodels.StringCheckGrader +import com.openai.models.graders.gradermodels.TextSimilarityGrader import com.openai.models.responses.ResponseInputText import java.util.Collections import java.util.Objects @@ -37,10 +41,10 @@ import kotlin.jvm.optionals.getOrNull /** * Create the structure of an evaluation that can be used to test a model's performance. An - * evaluation is a set of testing criteria and a datasource. After creating an evaluation, you can - * run it on different models and model parameters. We support several types of graders and - * datasources. For more information, see the - * [Evals guide](https://platform.openai.com/docs/guides/evals). + * evaluation is a set of testing criteria and the config for a data source, which dictates the + * schema of the data used in the evaluation. After creating an evaluation, you can run it on + * different models and model parameters. We support several types of graders and datasources. For + * more information, see the [Evals guide](https://platform.openai.com/docs/guides/evals). */ class EvalCreateParams private constructor( @@ -50,7 +54,8 @@ private constructor( ) : Params { /** - * The configuration for the data source used for the evaluation runs. + * The configuration for the data source used for the evaluation runs. Dictates the schema of + * the data used in the evaluation. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -58,7 +63,9 @@ private constructor( fun dataSourceConfig(): DataSourceConfig = body.dataSourceConfig() /** - * A list of graders for all eval runs in this group. + * A list of graders for all eval runs in this group. Graders can reference variables in the + * data source using double curly braces notation, like `{{item.variable_name}}`. To reference + * the model's output, use the `sample` namespace (ie, `{{sample.output_text}}`). * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -163,7 +170,10 @@ private constructor( */ fun body(body: Body) = apply { this.body = body.toBuilder() } - /** The configuration for the data source used for the evaluation runs. */ + /** + * The configuration for the data source used for the evaluation runs. Dictates the schema + * of the data used in the evaluation. + */ fun dataSourceConfig(dataSourceConfig: DataSourceConfig) = apply { body.dataSourceConfig(dataSourceConfig) } @@ -199,7 +209,20 @@ private constructor( /** Alias for calling [dataSourceConfig] with `DataSourceConfig.ofLogs(logs)`. */ fun dataSourceConfig(logs: DataSourceConfig.Logs) = apply { body.dataSourceConfig(logs) } - /** A list of graders for all eval runs in this group. */ + /** + * Alias for calling [dataSourceConfig] with + * `DataSourceConfig.ofStoredCompletions(storedCompletions)`. + */ + @Deprecated("deprecated") + fun dataSourceConfig(storedCompletions: DataSourceConfig.StoredCompletions) = apply { + body.dataSourceConfig(storedCompletions) + } + + /** + * A list of graders for all eval runs in this group. Graders can reference variables in the + * data source using double curly braces notation, like `{{item.variable_name}}`. To + * reference the model's output, use the `sample` namespace (ie, `{{sample.output_text}}`). + */ fun testingCriteria(testingCriteria: List) = apply { body.testingCriteria(testingCriteria) } @@ -235,7 +258,7 @@ private constructor( * Alias for calling [addTestingCriterion] with * `TestingCriterion.ofStringCheck(stringCheck)`. */ - fun addTestingCriterion(stringCheck: EvalStringCheckGrader) = apply { + fun addTestingCriterion(stringCheck: StringCheckGrader) = apply { body.addTestingCriterion(stringCheck) } @@ -243,7 +266,7 @@ private constructor( * Alias for calling [addTestingCriterion] with * `TestingCriterion.ofTextSimilarity(textSimilarity)`. */ - fun addTestingCriterion(textSimilarity: EvalTextSimilarityGrader) = apply { + fun addTestingCriterion(textSimilarity: TestingCriterion.TextSimilarity) = apply { body.addTestingCriterion(textSimilarity) } @@ -456,7 +479,8 @@ private constructor( ) : this(dataSourceConfig, testingCriteria, metadata, name, mutableMapOf()) /** - * The configuration for the data source used for the evaluation runs. + * The configuration for the data source used for the evaluation runs. Dictates the schema + * of the data used in the evaluation. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -465,7 +489,9 @@ private constructor( dataSourceConfig.getRequired("data_source_config") /** - * A list of graders for all eval runs in this group. + * A list of graders for all eval runs in this group. Graders can reference variables in the + * data source using double curly braces notation, like `{{item.variable_name}}`. To + * reference the model's output, use the `sample` namespace (ie, `{{sample.output_text}}`). * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -572,7 +598,10 @@ private constructor( additionalProperties = body.additionalProperties.toMutableMap() } - /** The configuration for the data source used for the evaluation runs. */ + /** + * The configuration for the data source used for the evaluation runs. Dictates the + * schema of the data used in the evaluation. + */ fun dataSourceConfig(dataSourceConfig: DataSourceConfig) = dataSourceConfig(JsonField.of(dataSourceConfig)) @@ -606,7 +635,20 @@ private constructor( fun dataSourceConfig(logs: DataSourceConfig.Logs) = dataSourceConfig(DataSourceConfig.ofLogs(logs)) - /** A list of graders for all eval runs in this group. */ + /** + * Alias for calling [dataSourceConfig] with + * `DataSourceConfig.ofStoredCompletions(storedCompletions)`. + */ + @Deprecated("deprecated") + fun dataSourceConfig(storedCompletions: DataSourceConfig.StoredCompletions) = + dataSourceConfig(DataSourceConfig.ofStoredCompletions(storedCompletions)) + + /** + * A list of graders for all eval runs in this group. Graders can reference variables in + * the data source using double curly braces notation, like `{{item.variable_name}}`. To + * reference the model's output, use the `sample` namespace (ie, + * `{{sample.output_text}}`). + */ fun testingCriteria(testingCriteria: List) = testingCriteria(JsonField.of(testingCriteria)) @@ -644,14 +686,14 @@ private constructor( * Alias for calling [addTestingCriterion] with * `TestingCriterion.ofStringCheck(stringCheck)`. */ - fun addTestingCriterion(stringCheck: EvalStringCheckGrader) = + fun addTestingCriterion(stringCheck: StringCheckGrader) = addTestingCriterion(TestingCriterion.ofStringCheck(stringCheck)) /** * Alias for calling [addTestingCriterion] with * `TestingCriterion.ofTextSimilarity(textSimilarity)`. */ - fun addTestingCriterion(textSimilarity: EvalTextSimilarityGrader) = + fun addTestingCriterion(textSimilarity: TestingCriterion.TextSimilarity) = addTestingCriterion(TestingCriterion.ofTextSimilarity(textSimilarity)) /** Alias for calling [addTestingCriterion] with `TestingCriterion.ofPython(python)`. */ @@ -794,13 +836,17 @@ private constructor( "Body{dataSourceConfig=$dataSourceConfig, testingCriteria=$testingCriteria, metadata=$metadata, name=$name, additionalProperties=$additionalProperties}" } - /** The configuration for the data source used for the evaluation runs. */ + /** + * The configuration for the data source used for the evaluation runs. Dictates the schema of + * the data used in the evaluation. + */ @JsonDeserialize(using = DataSourceConfig.Deserializer::class) @JsonSerialize(using = DataSourceConfig.Serializer::class) class DataSourceConfig private constructor( private val custom: Custom? = null, private val logs: Logs? = null, + private val storedCompletions: StoredCompletions? = null, private val _json: JsonValue? = null, ) { @@ -813,15 +859,22 @@ private constructor( fun custom(): Optional = Optional.ofNullable(custom) /** - * A data source config which specifies the metadata property of your stored completions - * query. This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. + * A data source config which specifies the metadata property of your logs query. This is + * usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. */ fun logs(): Optional = Optional.ofNullable(logs) + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") + fun storedCompletions(): Optional = + Optional.ofNullable(storedCompletions) + fun isCustom(): Boolean = custom != null fun isLogs(): Boolean = logs != null + @Deprecated("deprecated") fun isStoredCompletions(): Boolean = storedCompletions != null + /** * A CustomDataSourceConfig object that defines the schema for the data source used for the * evaluation runs. This schema is used to define the shape of the data that will be: @@ -831,17 +884,23 @@ private constructor( fun asCustom(): Custom = custom.getOrThrow("custom") /** - * A data source config which specifies the metadata property of your stored completions - * query. This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. + * A data source config which specifies the metadata property of your logs query. This is + * usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. */ fun asLogs(): Logs = logs.getOrThrow("logs") + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") + fun asStoredCompletions(): StoredCompletions = + storedCompletions.getOrThrow("storedCompletions") + fun _json(): Optional = Optional.ofNullable(_json) fun accept(visitor: Visitor): T = when { custom != null -> visitor.visitCustom(custom) logs != null -> visitor.visitLogs(logs) + storedCompletions != null -> visitor.visitStoredCompletions(storedCompletions) else -> visitor.unknown(_json) } @@ -861,6 +920,10 @@ private constructor( override fun visitLogs(logs: Logs) { logs.validate() } + + override fun visitStoredCompletions(storedCompletions: StoredCompletions) { + storedCompletions.validate() + } } ) validated = true @@ -888,6 +951,9 @@ private constructor( override fun visitLogs(logs: Logs) = logs.validity() + override fun visitStoredCompletions(storedCompletions: StoredCompletions) = + storedCompletions.validity() + override fun unknown(json: JsonValue?) = 0 } ) @@ -897,15 +963,17 @@ private constructor( return true } - return /* spotless:off */ other is DataSourceConfig && custom == other.custom && logs == other.logs /* spotless:on */ + return /* spotless:off */ other is DataSourceConfig && custom == other.custom && logs == other.logs && storedCompletions == other.storedCompletions /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(custom, logs) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(custom, logs, storedCompletions) /* spotless:on */ override fun toString(): String = when { custom != null -> "DataSourceConfig{custom=$custom}" logs != null -> "DataSourceConfig{logs=$logs}" + storedCompletions != null -> + "DataSourceConfig{storedCompletions=$storedCompletions}" _json != null -> "DataSourceConfig{_unknown=$_json}" else -> throw IllegalStateException("Invalid DataSourceConfig") } @@ -922,10 +990,16 @@ private constructor( @JvmStatic fun ofCustom(custom: Custom) = DataSourceConfig(custom = custom) /** - * A data source config which specifies the metadata property of your stored completions - * query. This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. + * A data source config which specifies the metadata property of your logs query. This + * is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. */ @JvmStatic fun ofLogs(logs: Logs) = DataSourceConfig(logs = logs) + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") + @JvmStatic + fun ofStoredCompletions(storedCompletions: StoredCompletions) = + DataSourceConfig(storedCompletions = storedCompletions) } /** @@ -944,11 +1018,15 @@ private constructor( fun visitCustom(custom: Custom): T /** - * A data source config which specifies the metadata property of your stored completions - * query. This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. + * A data source config which specifies the metadata property of your logs query. This + * is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. */ fun visitLogs(logs: Logs): T + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") + fun visitStoredCompletions(storedCompletions: StoredCompletions): T + /** * Maps an unknown variant of [DataSourceConfig] to a value of type [T]. * @@ -981,6 +1059,11 @@ private constructor( DataSourceConfig(logs = it, _json = json) } ?: DataSourceConfig(_json = json) } + "stored_completions" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DataSourceConfig(storedCompletions = it, _json = json) + } ?: DataSourceConfig(_json = json) + } } return DataSourceConfig(_json = json) @@ -997,6 +1080,8 @@ private constructor( when { value.custom != null -> generator.writeObject(value.custom) value.logs != null -> generator.writeObject(value.logs) + value.storedCompletions != null -> + generator.writeObject(value.storedCompletions) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid DataSourceConfig") } @@ -1373,8 +1458,8 @@ private constructor( } /** - * A data source config which specifies the metadata property of your stored completions - * query. This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. + * A data source config which specifies the metadata property of your logs query. This is + * usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. */ class Logs private constructor( @@ -1669,1148 +1754,1225 @@ private constructor( override fun toString() = "Logs{type=$type, metadata=$metadata, additionalProperties=$additionalProperties}" } - } - - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the evaluation. - */ - @JsonDeserialize(using = TestingCriterion.Deserializer::class) - @JsonSerialize(using = TestingCriterion.Serializer::class) - class TestingCriterion - private constructor( - private val labelModel: LabelModel? = null, - private val stringCheck: EvalStringCheckGrader? = null, - private val textSimilarity: EvalTextSimilarityGrader? = null, - private val python: Python? = null, - private val scoreModel: ScoreModel? = null, - private val _json: JsonValue? = null, - ) { - - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - fun labelModel(): Optional = Optional.ofNullable(labelModel) - /** - * A StringCheckGrader object that performs a string comparison between input and reference - * using a specified operation. - */ - fun stringCheck(): Optional = Optional.ofNullable(stringCheck) - - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun textSimilarity(): Optional = - Optional.ofNullable(textSimilarity) - - /** A PythonGrader object that runs a python script on the input. */ - fun python(): Optional = Optional.ofNullable(python) + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") + class StoredCompletions + private constructor( + private val type: JsonValue, + private val metadata: JsonField, + private val additionalProperties: MutableMap, + ) { - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun scoreModel(): Optional = Optional.ofNullable(scoreModel) + @JsonCreator + private constructor( + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("metadata") + @ExcludeMissing + metadata: JsonField = JsonMissing.of(), + ) : this(type, metadata, mutableMapOf()) - fun isLabelModel(): Boolean = labelModel != null + /** + * The type of data source. Always `stored_completions`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("stored_completions") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - fun isStringCheck(): Boolean = stringCheck != null + /** + * Metadata filters for the stored completions data source. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun metadata(): Optional = metadata.getOptional("metadata") - fun isTextSimilarity(): Boolean = textSimilarity != null + /** + * Returns the raw JSON value of [metadata]. + * + * Unlike [metadata], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("metadata") + @ExcludeMissing + fun _metadata(): JsonField = metadata - fun isPython(): Boolean = python != null + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } - fun isScoreModel(): Boolean = scoreModel != null + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - fun asLabelModel(): LabelModel = labelModel.getOrThrow("labelModel") + fun toBuilder() = Builder().from(this) - /** - * A StringCheckGrader object that performs a string comparison between input and reference - * using a specified operation. - */ - fun asStringCheck(): EvalStringCheckGrader = stringCheck.getOrThrow("stringCheck") + companion object { - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun asTextSimilarity(): EvalTextSimilarityGrader = - textSimilarity.getOrThrow("textSimilarity") + /** + * Returns a mutable builder for constructing an instance of [StoredCompletions]. + */ + @JvmStatic fun builder() = Builder() + } - /** A PythonGrader object that runs a python script on the input. */ - fun asPython(): Python = python.getOrThrow("python") + /** A builder for [StoredCompletions]. */ + class Builder internal constructor() { - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun asScoreModel(): ScoreModel = scoreModel.getOrThrow("scoreModel") + private var type: JsonValue = JsonValue.from("stored_completions") + private var metadata: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() - fun _json(): Optional = Optional.ofNullable(_json) + @JvmSynthetic + internal fun from(storedCompletions: StoredCompletions) = apply { + type = storedCompletions.type + metadata = storedCompletions.metadata + additionalProperties = storedCompletions.additionalProperties.toMutableMap() + } - fun accept(visitor: Visitor): T = - when { - labelModel != null -> visitor.visitLabelModel(labelModel) - stringCheck != null -> visitor.visitStringCheck(stringCheck) - textSimilarity != null -> visitor.visitTextSimilarity(textSimilarity) - python != null -> visitor.visitPython(python) - scoreModel != null -> visitor.visitScoreModel(scoreModel) - else -> visitor.unknown(_json) - } + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("stored_completions") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - private var validated: Boolean = false + /** Metadata filters for the stored completions data source. */ + fun metadata(metadata: Metadata) = metadata(JsonField.of(metadata)) - fun validate(): TestingCriterion = apply { - if (validated) { - return@apply - } + /** + * Sets [Builder.metadata] to an arbitrary JSON value. + * + * You should usually call [Builder.metadata] with a well-typed [Metadata] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun metadata(metadata: JsonField) = apply { this.metadata = metadata } - accept( - object : Visitor { - override fun visitLabelModel(labelModel: LabelModel) { - labelModel.validate() - } + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) { - stringCheck.validate() - } + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) { - textSimilarity.validate() + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) } - override fun visitPython(python: Python) { - python.validate() - } + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - override fun visitScoreModel(scoreModel: ScoreModel) { - scoreModel.validate() - } + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) } - ) - validated = true - } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false + /** + * Returns an immutable instance of [StoredCompletions]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): StoredCompletions = + StoredCompletions(type, metadata, additionalProperties.toMutableMap()) } - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitLabelModel(labelModel: LabelModel) = labelModel.validity() - - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) = - stringCheck.validity() - - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - textSimilarity.validity() - - override fun visitPython(python: Python) = python.validity() - - override fun visitScoreModel(scoreModel: ScoreModel) = scoreModel.validity() + private var validated: Boolean = false - override fun unknown(json: JsonValue?) = 0 + fun validate(): StoredCompletions = apply { + if (validated) { + return@apply } - ) - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + _type().let { + if (it != JsonValue.from("stored_completions")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + metadata().ifPresent { it.validate() } + validated = true } - return /* spotless:off */ other is TestingCriterion && labelModel == other.labelModel && stringCheck == other.stringCheck && textSimilarity == other.textSimilarity && python == other.python && scoreModel == other.scoreModel /* spotless:on */ - } - - override fun hashCode(): Int = /* spotless:off */ Objects.hash(labelModel, stringCheck, textSimilarity, python, scoreModel) /* spotless:on */ - - override fun toString(): String = - when { - labelModel != null -> "TestingCriterion{labelModel=$labelModel}" - stringCheck != null -> "TestingCriterion{stringCheck=$stringCheck}" - textSimilarity != null -> "TestingCriterion{textSimilarity=$textSimilarity}" - python != null -> "TestingCriterion{python=$python}" - scoreModel != null -> "TestingCriterion{scoreModel=$scoreModel}" - _json != null -> "TestingCriterion{_unknown=$_json}" - else -> throw IllegalStateException("Invalid TestingCriterion") - } - - companion object { - - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - @JvmStatic - fun ofLabelModel(labelModel: LabelModel) = TestingCriterion(labelModel = labelModel) + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } /** - * A StringCheckGrader object that performs a string comparison between input and - * reference using a specified operation. + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. */ - @JvmStatic - fun ofStringCheck(stringCheck: EvalStringCheckGrader) = - TestingCriterion(stringCheck = stringCheck) + @JvmSynthetic + internal fun validity(): Int = + type.let { if (it == JsonValue.from("stored_completions")) 1 else 0 } + + (metadata.asKnown().getOrNull()?.validity() ?: 0) - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - @JvmStatic - fun ofTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - TestingCriterion(textSimilarity = textSimilarity) + /** Metadata filters for the stored completions data source. */ + class Metadata + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { - /** A PythonGrader object that runs a python script on the input. */ - @JvmStatic fun ofPython(python: Python) = TestingCriterion(python = python) + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - @JvmStatic - fun ofScoreModel(scoreModel: ScoreModel) = TestingCriterion(scoreModel = scoreModel) - } + fun toBuilder() = Builder().from(this) - /** - * An interface that defines how to map each variant of [TestingCriterion] to a value of - * type [T]. - */ - interface Visitor { + companion object { - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - fun visitLabelModel(labelModel: LabelModel): T + /** Returns a mutable builder for constructing an instance of [Metadata]. */ + @JvmStatic fun builder() = Builder() + } - /** - * A StringCheckGrader object that performs a string comparison between input and - * reference using a specified operation. - */ - fun visitStringCheck(stringCheck: EvalStringCheckGrader): T + /** A builder for [Metadata]. */ + class Builder internal constructor() { - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader): T + private var additionalProperties: MutableMap = mutableMapOf() - /** A PythonGrader object that runs a python script on the input. */ - fun visitPython(python: Python): T + @JvmSynthetic + internal fun from(metadata: Metadata) = apply { + additionalProperties = metadata.additionalProperties.toMutableMap() + } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun visitScoreModel(scoreModel: ScoreModel): T + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - /** - * Maps an unknown variant of [TestingCriterion] to a value of type [T]. - * - * An instance of [TestingCriterion] 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 TestingCriterion: $json") - } - } + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - internal class Deserializer : BaseDeserializer(TestingCriterion::class) { + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } - override fun ObjectCodec.deserialize(node: JsonNode): TestingCriterion { - val json = JsonValue.fromJsonNode(node) - val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - when (type) { - "label_model" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(labelModel = it, _json = json) - } ?: TestingCriterion(_json = json) + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) } - "string_check" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(stringCheck = it, _json = json) - } ?: TestingCriterion(_json = json) + + /** + * Returns an immutable instance of [Metadata]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Metadata = Metadata(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): Metadata = apply { + if (validated) { + return@apply } - "text_similarity" -> { - return tryDeserialize(node, jacksonTypeRef()) - ?.let { TestingCriterion(textSimilarity = it, _json = json) } - ?: TestingCriterion(_json = json) + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false } - "python" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(python = it, _json = json) - } ?: TestingCriterion(_json = json) + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> + !value.isNull() && !value.isMissing() } - "score_model" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(scoreModel = it, _json = json) - } ?: TestingCriterion(_json = json) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } + + return /* spotless:off */ other is Metadata && additionalProperties == other.additionalProperties /* spotless:on */ } - return TestingCriterion(_json = json) - } - } + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ - internal class Serializer : BaseSerializer(TestingCriterion::class) { + override fun hashCode(): Int = hashCode - override fun serialize( - value: TestingCriterion, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.labelModel != null -> generator.writeObject(value.labelModel) - value.stringCheck != null -> generator.writeObject(value.stringCheck) - value.textSimilarity != null -> generator.writeObject(value.textSimilarity) - value.python != null -> generator.writeObject(value.python) - value.scoreModel != null -> generator.writeObject(value.scoreModel) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid TestingCriterion") + override fun toString() = "Metadata{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } + + return /* spotless:off */ other is StoredCompletions && type == other.type && metadata == other.metadata && additionalProperties == other.additionalProperties /* spotless:on */ } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(type, metadata, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "StoredCompletions{type=$type, metadata=$metadata, additionalProperties=$additionalProperties}" } + } + + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the evaluation. + */ + @JsonDeserialize(using = TestingCriterion.Deserializer::class) + @JsonSerialize(using = TestingCriterion.Serializer::class) + class TestingCriterion + private constructor( + private val labelModel: LabelModel? = null, + private val stringCheck: StringCheckGrader? = null, + private val textSimilarity: TextSimilarity? = null, + private val python: Python? = null, + private val scoreModel: ScoreModel? = null, + private val _json: JsonValue? = null, + ) { /** * A LabelModelGrader object which uses a model to assign labels to each item in the * evaluation. */ - class LabelModel - private constructor( - private val input: JsonField>, - private val labels: JsonField>, - private val model: JsonField, - private val name: JsonField, - private val passingLabels: JsonField>, - private val type: JsonValue, - private val additionalProperties: MutableMap, - ) { + fun labelModel(): Optional = Optional.ofNullable(labelModel) - @JsonCreator - private constructor( - @JsonProperty("input") - @ExcludeMissing - input: JsonField> = JsonMissing.of(), - @JsonProperty("labels") - @ExcludeMissing - labels: JsonField> = JsonMissing.of(), - @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), - @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), - @JsonProperty("passing_labels") - @ExcludeMissing - passingLabels: JsonField> = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - ) : this(input, labels, model, name, passingLabels, type, mutableMapOf()) + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun stringCheck(): Optional = Optional.ofNullable(stringCheck) - /** - * A list of chat messages forming the prompt or context. May include variable - * references to the "item" namespace, ie {{item.name}}. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun input(): List = input.getRequired("input") + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun textSimilarity(): Optional = Optional.ofNullable(textSimilarity) - /** - * The labels to classify to each item in the evaluation. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun labels(): List = labels.getRequired("labels") + /** A PythonGrader object that runs a python script on the input. */ + fun python(): Optional = Optional.ofNullable(python) - /** - * The model to use for the evaluation. Must support structured outputs. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun model(): String = model.getRequired("model") + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun scoreModel(): Optional = Optional.ofNullable(scoreModel) - /** - * The name of the grader. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun name(): String = name.getRequired("name") + fun isLabelModel(): Boolean = labelModel != null - /** - * The labels that indicate a passing result. Must be a subset of labels. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun passingLabels(): List = passingLabels.getRequired("passing_labels") + fun isStringCheck(): Boolean = stringCheck != null - /** - * The object type, which is always `label_model`. - * - * Expected to always return the following: - * ```java - * JsonValue.from("label_model") - * ``` - * - * However, this method can be useful for debugging and logging (e.g. if the server - * responded with an unexpected value). - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + fun isTextSimilarity(): Boolean = textSimilarity != null - /** - * Returns the raw JSON value of [input]. - * - * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("input") @ExcludeMissing fun _input(): JsonField> = input + fun isPython(): Boolean = python != null - /** - * Returns the raw JSON value of [labels]. - * - * Unlike [labels], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("labels") @ExcludeMissing fun _labels(): JsonField> = labels + fun isScoreModel(): Boolean = scoreModel != null - /** - * Returns the raw JSON value of [model]. - * - * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + fun asLabelModel(): LabelModel = labelModel.getOrThrow("labelModel") - /** - * Returns the raw JSON value of [name]. - * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun asStringCheck(): StringCheckGrader = stringCheck.getOrThrow("stringCheck") - /** - * Returns the raw JSON value of [passingLabels]. - * - * Unlike [passingLabels], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("passing_labels") - @ExcludeMissing - fun _passingLabels(): JsonField> = passingLabels + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun asTextSimilarity(): TextSimilarity = textSimilarity.getOrThrow("textSimilarity") - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } + /** A PythonGrader object that runs a python script on the input. */ + fun asPython(): Python = python.getOrThrow("python") - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun asScoreModel(): ScoreModel = scoreModel.getOrThrow("scoreModel") - fun toBuilder() = Builder().from(this) + fun _json(): Optional = Optional.ofNullable(_json) - companion object { + fun accept(visitor: Visitor): T = + when { + labelModel != null -> visitor.visitLabelModel(labelModel) + stringCheck != null -> visitor.visitStringCheck(stringCheck) + textSimilarity != null -> visitor.visitTextSimilarity(textSimilarity) + python != null -> visitor.visitPython(python) + scoreModel != null -> visitor.visitScoreModel(scoreModel) + else -> visitor.unknown(_json) + } - /** - * Returns a mutable builder for constructing an instance of [LabelModel]. - * - * The following fields are required: - * ```java - * .input() - * .labels() - * .model() - * .name() - * .passingLabels() - * ``` - */ - @JvmStatic fun builder() = Builder() + private var validated: Boolean = false + + fun validate(): TestingCriterion = apply { + if (validated) { + return@apply } - /** A builder for [LabelModel]. */ - class Builder internal constructor() { + accept( + object : Visitor { + override fun visitLabelModel(labelModel: LabelModel) { + labelModel.validate() + } - private var input: JsonField>? = null - private var labels: JsonField>? = null - private var model: JsonField? = null - private var name: JsonField? = null - private var passingLabels: JsonField>? = null - private var type: JsonValue = JsonValue.from("label_model") - private var additionalProperties: MutableMap = mutableMapOf() + override fun visitStringCheck(stringCheck: StringCheckGrader) { + stringCheck.validate() + } - @JvmSynthetic - internal fun from(labelModel: LabelModel) = apply { - input = labelModel.input.map { it.toMutableList() } - labels = labelModel.labels.map { it.toMutableList() } - model = labelModel.model - name = labelModel.name - passingLabels = labelModel.passingLabels.map { it.toMutableList() } - type = labelModel.type - additionalProperties = labelModel.additionalProperties.toMutableMap() - } + override fun visitTextSimilarity(textSimilarity: TextSimilarity) { + textSimilarity.validate() + } - /** - * A list of chat messages forming the prompt or context. May include variable - * references to the "item" namespace, ie {{item.name}}. - */ - fun input(input: List) = input(JsonField.of(input)) + override fun visitPython(python: Python) { + python.validate() + } - /** - * Sets [Builder.input] to an arbitrary JSON value. - * - * You should usually call [Builder.input] with a well-typed `List` value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun input(input: JsonField>) = apply { - this.input = input.map { it.toMutableList() } + override fun visitScoreModel(scoreModel: ScoreModel) { + scoreModel.validate() + } } + ) + validated = true + } - /** - * Adds a single [Input] to [Builder.input]. - * - * @throws IllegalStateException if the field was previously set to a non-list. - */ - fun addInput(input: Input) = apply { - this.input = - (this.input ?: JsonField.of(mutableListOf())).also { - checkKnown("input", it).add(input) - } - } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - /** - * Alias for calling [addInput] with - * `Input.ofSimpleInputMessage(simpleInputMessage)`. - */ - fun addInput(simpleInputMessage: Input.SimpleInputMessage) = - addInput(Input.ofSimpleInputMessage(simpleInputMessage)) + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitLabelModel(labelModel: LabelModel) = labelModel.validity() - /** Alias for calling [addInput] with `Input.ofEvalItem(evalItem)`. */ - fun addInput(evalItem: Input.EvalItem) = addInput(Input.ofEvalItem(evalItem)) + override fun visitStringCheck(stringCheck: StringCheckGrader) = + stringCheck.validity() - /** The labels to classify to each item in the evaluation. */ - fun labels(labels: List) = labels(JsonField.of(labels)) + override fun visitTextSimilarity(textSimilarity: TextSimilarity) = + textSimilarity.validity() - /** - * Sets [Builder.labels] to an arbitrary JSON value. - * - * You should usually call [Builder.labels] with a well-typed `List` value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun labels(labels: JsonField>) = apply { - this.labels = labels.map { it.toMutableList() } + override fun visitPython(python: Python) = python.validity() + + override fun visitScoreModel(scoreModel: ScoreModel) = scoreModel.validity() + + override fun unknown(json: JsonValue?) = 0 } + ) - /** - * Adds a single [String] to [labels]. - * - * @throws IllegalStateException if the field was previously set to a non-list. - */ - fun addLabel(label: String) = apply { - labels = - (labels ?: JsonField.of(mutableListOf())).also { - checkKnown("labels", it).add(label) - } - } + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - /** The model to use for the evaluation. Must support structured outputs. */ - fun model(model: String) = model(JsonField.of(model)) + return /* spotless:off */ other is TestingCriterion && labelModel == other.labelModel && stringCheck == other.stringCheck && textSimilarity == other.textSimilarity && python == other.python && scoreModel == other.scoreModel /* spotless:on */ + } - /** - * Sets [Builder.model] to an arbitrary JSON value. - * - * You should usually call [Builder.model] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun model(model: JsonField) = apply { this.model = model } + override fun hashCode(): Int = /* spotless:off */ Objects.hash(labelModel, stringCheck, textSimilarity, python, scoreModel) /* spotless:on */ - /** The name of the grader. */ - fun name(name: String) = name(JsonField.of(name)) + override fun toString(): String = + when { + labelModel != null -> "TestingCriterion{labelModel=$labelModel}" + stringCheck != null -> "TestingCriterion{stringCheck=$stringCheck}" + textSimilarity != null -> "TestingCriterion{textSimilarity=$textSimilarity}" + python != null -> "TestingCriterion{python=$python}" + scoreModel != null -> "TestingCriterion{scoreModel=$scoreModel}" + _json != null -> "TestingCriterion{_unknown=$_json}" + else -> throw IllegalStateException("Invalid TestingCriterion") + } - /** - * Sets [Builder.name] to an arbitrary JSON value. - * - * You should usually call [Builder.name] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun name(name: JsonField) = apply { this.name = name } + companion object { - /** The labels that indicate a passing result. Must be a subset of labels. */ - fun passingLabels(passingLabels: List) = - passingLabels(JsonField.of(passingLabels)) + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + @JvmStatic + fun ofLabelModel(labelModel: LabelModel) = TestingCriterion(labelModel = labelModel) - /** - * Sets [Builder.passingLabels] to an arbitrary JSON value. - * - * You should usually call [Builder.passingLabels] with a well-typed `List` - * value instead. This method is primarily for setting the field to an undocumented - * or not yet supported value. - */ - fun passingLabels(passingLabels: JsonField>) = apply { - this.passingLabels = passingLabels.map { it.toMutableList() } - } + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + @JvmStatic + fun ofStringCheck(stringCheck: StringCheckGrader) = + TestingCriterion(stringCheck = stringCheck) - /** - * Adds a single [String] to [passingLabels]. - * - * @throws IllegalStateException if the field was previously set to a non-list. - */ - fun addPassingLabel(passingLabel: String) = apply { - passingLabels = - (passingLabels ?: JsonField.of(mutableListOf())).also { - checkKnown("passingLabels", it).add(passingLabel) - } - } + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + @JvmStatic + fun ofTextSimilarity(textSimilarity: TextSimilarity) = + TestingCriterion(textSimilarity = textSimilarity) - /** - * Sets the field to an arbitrary JSON value. - * - * It is usually unnecessary to call this method because the field defaults to the - * following: - * ```java - * JsonValue.from("label_model") - * ``` - * - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun type(type: JsonValue) = apply { this.type = type } + /** A PythonGrader object that runs a python script on the input. */ + @JvmStatic fun ofPython(python: Python) = TestingCriterion(python = python) - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + @JvmStatic + fun ofScoreModel(scoreModel: ScoreModel) = TestingCriterion(scoreModel = scoreModel) + } - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + /** + * An interface that defines how to map each variant of [TestingCriterion] to a value of + * type [T]. + */ + interface Visitor { - fun putAllAdditionalProperties(additionalProperties: Map) = - apply { - this.additionalProperties.putAll(additionalProperties) - } + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + fun visitLabelModel(labelModel: LabelModel): T - fun removeAdditionalProperty(key: String) = apply { - additionalProperties.remove(key) - } + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + fun visitStringCheck(stringCheck: StringCheckGrader): T - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun visitTextSimilarity(textSimilarity: TextSimilarity): T - /** - * Returns an immutable instance of [LabelModel]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .input() - * .labels() - * .model() - * .name() - * .passingLabels() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): LabelModel = - LabelModel( - checkRequired("input", input).map { it.toImmutable() }, - checkRequired("labels", labels).map { it.toImmutable() }, - checkRequired("model", model), - checkRequired("name", name), - checkRequired("passingLabels", passingLabels).map { it.toImmutable() }, - type, - additionalProperties.toMutableMap(), - ) + /** A PythonGrader object that runs a python script on the input. */ + fun visitPython(python: Python): T + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun visitScoreModel(scoreModel: ScoreModel): T + + /** + * Maps an unknown variant of [TestingCriterion] to a value of type [T]. + * + * An instance of [TestingCriterion] 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 TestingCriterion: $json") } + } - private var validated: Boolean = false + internal class Deserializer : BaseDeserializer(TestingCriterion::class) { - fun validate(): LabelModel = apply { - if (validated) { - return@apply - } + override fun ObjectCodec.deserialize(node: JsonNode): TestingCriterion { + val json = JsonValue.fromJsonNode(node) + val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() - input().forEach { it.validate() } - labels() - model() - name() - passingLabels() - _type().let { - if (it != JsonValue.from("label_model")) { - throw OpenAIInvalidDataException("'type' is invalid, received $it") + when (type) { + "label_model" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(labelModel = it, _json = json) + } ?: TestingCriterion(_json = json) + } + "string_check" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(stringCheck = it, _json = json) + } ?: TestingCriterion(_json = json) + } + "text_similarity" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(textSimilarity = it, _json = json) + } ?: TestingCriterion(_json = json) + } + "python" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(python = it, _json = json) + } ?: TestingCriterion(_json = json) + } + "score_model" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(scoreModel = it, _json = json) + } ?: TestingCriterion(_json = json) } } - validated = true + + return TestingCriterion(_json = json) } + } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false + internal class Serializer : BaseSerializer(TestingCriterion::class) { + + override fun serialize( + value: TestingCriterion, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.labelModel != null -> generator.writeObject(value.labelModel) + value.stringCheck != null -> generator.writeObject(value.stringCheck) + value.textSimilarity != null -> generator.writeObject(value.textSimilarity) + value.python != null -> generator.writeObject(value.python) + value.scoreModel != null -> generator.writeObject(value.scoreModel) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid TestingCriterion") } + } + } - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (input.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + - (labels.asKnown().getOrNull()?.size ?: 0) + - (if (model.asKnown().isPresent) 1 else 0) + - (if (name.asKnown().isPresent) 1 else 0) + - (passingLabels.asKnown().getOrNull()?.size ?: 0) + - type.let { if (it == JsonValue.from("label_model")) 1 else 0 } + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + class LabelModel + private constructor( + private val input: JsonField>, + private val labels: JsonField>, + private val model: JsonField, + private val name: JsonField, + private val passingLabels: JsonField>, + private val type: JsonValue, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("input") + @ExcludeMissing + input: JsonField> = JsonMissing.of(), + @JsonProperty("labels") + @ExcludeMissing + labels: JsonField> = JsonMissing.of(), + @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("passing_labels") + @ExcludeMissing + passingLabels: JsonField> = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + ) : this(input, labels, model, name, passingLabels, type, mutableMapOf()) /** - * A chat message that makes up the prompt or context. May include variable references - * to the "item" namespace, ie {{item.name}}. + * A list of chat messages forming the prompt or context. May include variable + * references to the `item` namespace, ie {{item.name}}. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). */ - @JsonDeserialize(using = Input.Deserializer::class) - @JsonSerialize(using = Input.Serializer::class) - class Input - private constructor( - private val simpleInputMessage: SimpleInputMessage? = null, - private val evalItem: EvalItem? = null, - private val _json: JsonValue? = null, - ) { + fun input(): List = input.getRequired("input") - fun simpleInputMessage(): Optional = - Optional.ofNullable(simpleInputMessage) + /** + * The labels to classify to each item in the evaluation. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun labels(): List = labels.getRequired("labels") - /** - * A message input to the model with a role indicating instruction following - * hierarchy. Instructions given with the `developer` or `system` role take - * precedence over instructions given with the `user` role. Messages with the - * `assistant` role are presumed to have been generated by the model in previous - * interactions. - */ - fun evalItem(): Optional = Optional.ofNullable(evalItem) + /** + * The model to use for the evaluation. Must support structured outputs. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun model(): String = model.getRequired("model") - fun isSimpleInputMessage(): Boolean = simpleInputMessage != null + /** + * The name of the grader. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun name(): String = name.getRequired("name") - fun isEvalItem(): Boolean = evalItem != null + /** + * The labels that indicate a passing result. Must be a subset of labels. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun passingLabels(): List = passingLabels.getRequired("passing_labels") - fun asSimpleInputMessage(): SimpleInputMessage = - simpleInputMessage.getOrThrow("simpleInputMessage") + /** + * The object type, which is always `label_model`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("label_model") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - /** - * A message input to the model with a role indicating instruction following - * hierarchy. Instructions given with the `developer` or `system` role take - * precedence over instructions given with the `user` role. Messages with the - * `assistant` role are presumed to have been generated by the model in previous - * interactions. - */ - fun asEvalItem(): EvalItem = evalItem.getOrThrow("evalItem") + /** + * Returns the raw JSON value of [input]. + * + * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("input") @ExcludeMissing fun _input(): JsonField> = input - fun _json(): Optional = Optional.ofNullable(_json) + /** + * Returns the raw JSON value of [labels]. + * + * Unlike [labels], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("labels") @ExcludeMissing fun _labels(): JsonField> = labels - fun accept(visitor: Visitor): T = - when { - simpleInputMessage != null -> - visitor.visitSimpleInputMessage(simpleInputMessage) - evalItem != null -> visitor.visitEvalItem(evalItem) - else -> visitor.unknown(_json) - } + /** + * Returns the raw JSON value of [model]. + * + * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model - private var validated: Boolean = false + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name - fun validate(): Input = apply { - if (validated) { - return@apply - } + /** + * Returns the raw JSON value of [passingLabels]. + * + * Unlike [passingLabels], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("passing_labels") + @ExcludeMissing + fun _passingLabels(): JsonField> = passingLabels - accept( - object : Visitor { - override fun visitSimpleInputMessage( - simpleInputMessage: SimpleInputMessage - ) { - simpleInputMessage.validate() - } + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } - override fun visitEvalItem(evalItem: EvalItem) { - evalItem.validate() - } - } - ) - validated = true - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + fun toBuilder() = Builder().from(this) + + companion object { /** - * Returns a score indicating how many valid values are contained in this object - * recursively. + * Returns a mutable builder for constructing an instance of [LabelModel]. * - * Used for best match union deserialization. + * The following fields are required: + * ```java + * .input() + * .labels() + * .model() + * .name() + * .passingLabels() + * ``` */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitSimpleInputMessage( - simpleInputMessage: SimpleInputMessage - ) = simpleInputMessage.validity() - - override fun visitEvalItem(evalItem: EvalItem) = evalItem.validity() + @JvmStatic fun builder() = Builder() + } - override fun unknown(json: JsonValue?) = 0 - } - ) + /** A builder for [LabelModel]. */ + class Builder internal constructor() { - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + private var input: JsonField>? = null + private var labels: JsonField>? = null + private var model: JsonField? = null + private var name: JsonField? = null + private var passingLabels: JsonField>? = null + private var type: JsonValue = JsonValue.from("label_model") + private var additionalProperties: MutableMap = mutableMapOf() - return /* spotless:off */ other is Input && simpleInputMessage == other.simpleInputMessage && evalItem == other.evalItem /* spotless:on */ + @JvmSynthetic + internal fun from(labelModel: LabelModel) = apply { + input = labelModel.input.map { it.toMutableList() } + labels = labelModel.labels.map { it.toMutableList() } + model = labelModel.model + name = labelModel.name + passingLabels = labelModel.passingLabels.map { it.toMutableList() } + type = labelModel.type + additionalProperties = labelModel.additionalProperties.toMutableMap() } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(simpleInputMessage, evalItem) /* spotless:on */ - - override fun toString(): String = - when { - simpleInputMessage != null -> - "Input{simpleInputMessage=$simpleInputMessage}" - evalItem != null -> "Input{evalItem=$evalItem}" - _json != null -> "Input{_unknown=$_json}" - else -> throw IllegalStateException("Invalid Input") - } - - companion object { + /** + * A list of chat messages forming the prompt or context. May include variable + * references to the `item` namespace, ie {{item.name}}. + */ + fun input(input: List) = input(JsonField.of(input)) - @JvmStatic - fun ofSimpleInputMessage(simpleInputMessage: SimpleInputMessage) = - Input(simpleInputMessage = simpleInputMessage) + /** + * Sets [Builder.input] to an arbitrary JSON value. + * + * You should usually call [Builder.input] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun input(input: JsonField>) = apply { + this.input = input.map { it.toMutableList() } + } - /** - * A message input to the model with a role indicating instruction following - * hierarchy. Instructions given with the `developer` or `system` role take - * precedence over instructions given with the `user` role. Messages with the - * `assistant` role are presumed to have been generated by the model in previous - * interactions. - */ - @JvmStatic fun ofEvalItem(evalItem: EvalItem) = Input(evalItem = evalItem) + /** + * Adds a single [Input] to [Builder.input]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addInput(input: Input) = apply { + this.input = + (this.input ?: JsonField.of(mutableListOf())).also { + checkKnown("input", it).add(input) + } } /** - * An interface that defines how to map each variant of [Input] to a value of type - * [T]. + * Alias for calling [addInput] with + * `Input.ofSimpleInputMessage(simpleInputMessage)`. */ - interface Visitor { + fun addInput(simpleInputMessage: Input.SimpleInputMessage) = + addInput(Input.ofSimpleInputMessage(simpleInputMessage)) - fun visitSimpleInputMessage(simpleInputMessage: SimpleInputMessage): T + /** Alias for calling [addInput] with `Input.ofEvalItem(evalItem)`. */ + fun addInput(evalItem: Input.EvalItem) = addInput(Input.ofEvalItem(evalItem)) - /** - * A message input to the model with a role indicating instruction following - * hierarchy. Instructions given with the `developer` or `system` role take - * precedence over instructions given with the `user` role. Messages with the - * `assistant` role are presumed to have been generated by the model in previous - * interactions. - */ - fun visitEvalItem(evalItem: EvalItem): T + /** The labels to classify to each item in the evaluation. */ + fun labels(labels: List) = labels(JsonField.of(labels)) - /** - * Maps an unknown variant of [Input] to a value of type [T]. - * - * An instance of [Input] 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 Input: $json") - } + /** + * Sets [Builder.labels] to an arbitrary JSON value. + * + * You should usually call [Builder.labels] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun labels(labels: JsonField>) = apply { + this.labels = labels.map { it.toMutableList() } } - internal class Deserializer : BaseDeserializer(Input::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): Input { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { Input(simpleInputMessage = it, _json = json) }, - tryDeserialize(node, jacksonTypeRef())?.let { - Input(evalItem = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing from - // boolean). - 0 -> Input(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then use - // the first completely valid match, or simply the first match if none - // are completely valid. - else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + /** + * Adds a single [String] to [labels]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addLabel(label: String) = apply { + labels = + (labels ?: JsonField.of(mutableListOf())).also { + checkKnown("labels", it).add(label) } - } } - internal class Serializer : BaseSerializer(Input::class) { + /** The model to use for the evaluation. Must support structured outputs. */ + fun model(model: String) = model(JsonField.of(model)) - override fun serialize( - value: Input, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.simpleInputMessage != null -> - generator.writeObject(value.simpleInputMessage) - value.evalItem != null -> generator.writeObject(value.evalItem) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid Input") - } - } - } + /** + * Sets [Builder.model] to an arbitrary JSON value. + * + * You should usually call [Builder.model] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun model(model: JsonField) = apply { this.model = model } - class SimpleInputMessage - private constructor( - private val content: JsonField, - private val role: JsonField, - private val additionalProperties: MutableMap, - ) { + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) - @JsonCreator - private constructor( - @JsonProperty("content") - @ExcludeMissing - content: JsonField = JsonMissing.of(), - @JsonProperty("role") - @ExcludeMissing - role: JsonField = JsonMissing.of(), - ) : this(content, role, mutableMapOf()) + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun name(name: JsonField) = apply { this.name = name } - /** - * The content of the message. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type - * or is unexpectedly missing or null (e.g. if the server responded with an - * unexpected value). - */ - fun content(): String = content.getRequired("content") + /** The labels that indicate a passing result. Must be a subset of labels. */ + fun passingLabels(passingLabels: List) = + passingLabels(JsonField.of(passingLabels)) - /** - * The role of the message (e.g. "system", "assistant", "user"). - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type - * or is unexpectedly missing or null (e.g. if the server responded with an - * unexpected value). - */ - fun role(): String = role.getRequired("role") + /** + * Sets [Builder.passingLabels] to an arbitrary JSON value. + * + * You should usually call [Builder.passingLabels] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun passingLabels(passingLabels: JsonField>) = apply { + this.passingLabels = passingLabels.map { it.toMutableList() } + } - /** - * Returns the raw JSON value of [content]. - * - * Unlike [content], this method doesn't throw if the JSON field has an - * unexpected type. - */ - @JsonProperty("content") - @ExcludeMissing - fun _content(): JsonField = content + /** + * Adds a single [String] to [passingLabels]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addPassingLabel(passingLabel: String) = apply { + passingLabels = + (passingLabels ?: JsonField.of(mutableListOf())).also { + checkKnown("passingLabels", it).add(passingLabel) + } + } - /** - * Returns the raw JSON value of [role]. - * - * Unlike [role], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("role") @ExcludeMissing fun _role(): JsonField = role + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("label_model") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - fun toBuilder() = Builder().from(this) + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } - companion object { + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - /** - * Returns a mutable builder for constructing an instance of - * [SimpleInputMessage]. - * - * The following fields are required: - * ```java - * .content() - * .role() - * ``` - */ - @JvmStatic fun builder() = Builder() + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [LabelModel]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .input() + * .labels() + * .model() + * .name() + * .passingLabels() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): LabelModel = + LabelModel( + checkRequired("input", input).map { it.toImmutable() }, + checkRequired("labels", labels).map { it.toImmutable() }, + checkRequired("model", model), + checkRequired("name", name), + checkRequired("passingLabels", passingLabels).map { it.toImmutable() }, + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): LabelModel = apply { + if (validated) { + return@apply + } + + input().forEach { it.validate() } + labels() + model() + name() + passingLabels() + _type().let { + if (it != JsonValue.from("label_model")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") } + } + validated = true + } - /** A builder for [SimpleInputMessage]. */ - class Builder internal constructor() { + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - private var content: JsonField? = null - private var role: JsonField? = null - private var additionalProperties: MutableMap = - mutableMapOf() + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (input.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (labels.asKnown().getOrNull()?.size ?: 0) + + (if (model.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (passingLabels.asKnown().getOrNull()?.size ?: 0) + + type.let { if (it == JsonValue.from("label_model")) 1 else 0 } - @JvmSynthetic - internal fun from(simpleInputMessage: SimpleInputMessage) = apply { - content = simpleInputMessage.content - role = simpleInputMessage.role - additionalProperties = - simpleInputMessage.additionalProperties.toMutableMap() - } + /** + * A chat message that makes up the prompt or context. May include variable references + * to the `item` namespace, ie {{item.name}}. + */ + @JsonDeserialize(using = Input.Deserializer::class) + @JsonSerialize(using = Input.Serializer::class) + class Input + private constructor( + private val simpleInputMessage: SimpleInputMessage? = null, + private val evalItem: EvalItem? = null, + private val _json: JsonValue? = null, + ) { - /** The content of the message. */ - fun content(content: String) = content(JsonField.of(content)) + fun simpleInputMessage(): Optional = + Optional.ofNullable(simpleInputMessage) - /** - * Sets [Builder.content] to an arbitrary JSON value. - * - * You should usually call [Builder.content] with a well-typed [String] - * value instead. This method is primarily for setting the field to an - * undocumented or not yet supported value. - */ - fun content(content: JsonField) = apply { this.content = content } + /** + * A message input to the model with a role indicating instruction following + * hierarchy. Instructions given with the `developer` or `system` role take + * precedence over instructions given with the `user` role. Messages with the + * `assistant` role are presumed to have been generated by the model in previous + * interactions. + */ + fun evalItem(): Optional = Optional.ofNullable(evalItem) - /** The role of the message (e.g. "system", "assistant", "user"). */ - fun role(role: String) = role(JsonField.of(role)) + fun isSimpleInputMessage(): Boolean = simpleInputMessage != null - /** - * Sets [Builder.role] to an arbitrary JSON value. - * - * You should usually call [Builder.role] with a well-typed [String] value - * instead. This method is primarily for setting the field to an - * undocumented or not yet supported value. - */ - fun role(role: JsonField) = apply { this.role = role } + fun isEvalItem(): Boolean = evalItem != null - fun additionalProperties(additionalProperties: Map) = - apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + fun asSimpleInputMessage(): SimpleInputMessage = + simpleInputMessage.getOrThrow("simpleInputMessage") - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + /** + * A message input to the model with a role indicating instruction following + * hierarchy. Instructions given with the `developer` or `system` role take + * precedence over instructions given with the `user` role. Messages with the + * `assistant` role are presumed to have been generated by the model in previous + * interactions. + */ + fun asEvalItem(): EvalItem = evalItem.getOrThrow("evalItem") - fun putAllAdditionalProperties( - additionalProperties: Map - ) = apply { this.additionalProperties.putAll(additionalProperties) } + fun _json(): Optional = Optional.ofNullable(_json) - fun removeAdditionalProperty(key: String) = apply { - additionalProperties.remove(key) - } + fun accept(visitor: Visitor): T = + when { + simpleInputMessage != null -> + visitor.visitSimpleInputMessage(simpleInputMessage) + evalItem != null -> visitor.visitEvalItem(evalItem) + else -> visitor.unknown(_json) + } - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + private var validated: Boolean = false - /** - * Returns an immutable instance of [SimpleInputMessage]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .content() - * .role() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): SimpleInputMessage = - SimpleInputMessage( - checkRequired("content", content), - checkRequired("role", role), - additionalProperties.toMutableMap(), - ) + fun validate(): Input = apply { + if (validated) { + return@apply } - private var validated: Boolean = false + accept( + object : Visitor { + override fun visitSimpleInputMessage( + simpleInputMessage: SimpleInputMessage + ) { + simpleInputMessage.validate() + } - fun validate(): SimpleInputMessage = apply { - if (validated) { - return@apply + override fun visitEvalItem(evalItem: EvalItem) { + evalItem.validate() + } } + ) + validated = true + } - content() - role() - validated = true + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitSimpleInputMessage( + simpleInputMessage: SimpleInputMessage + ) = simpleInputMessage.validity() - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (if (content.asKnown().isPresent) 1 else 0) + - (if (role.asKnown().isPresent) 1 else 0) + override fun visitEvalItem(evalItem: EvalItem) = evalItem.validity() - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + override fun unknown(json: JsonValue?) = 0 } + ) - return /* spotless:off */ other is SimpleInputMessage && content == other.content && role == other.role && additionalProperties == other.additionalProperties /* spotless:on */ - } - - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(content, role, additionalProperties) } - /* spotless:on */ + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - override fun hashCode(): Int = hashCode + return /* spotless:off */ other is Input && simpleInputMessage == other.simpleInputMessage && evalItem == other.evalItem /* spotless:on */ + } - override fun toString() = - "SimpleInputMessage{content=$content, role=$role, additionalProperties=$additionalProperties}" + override fun hashCode(): Int = /* spotless:off */ Objects.hash(simpleInputMessage, evalItem) /* spotless:on */ + + override fun toString(): String = + when { + simpleInputMessage != null -> + "Input{simpleInputMessage=$simpleInputMessage}" + evalItem != null -> "Input{evalItem=$evalItem}" + _json != null -> "Input{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Input") + } + + companion object { + + @JvmStatic + fun ofSimpleInputMessage(simpleInputMessage: SimpleInputMessage) = + Input(simpleInputMessage = simpleInputMessage) + + /** + * A message input to the model with a role indicating instruction following + * hierarchy. Instructions given with the `developer` or `system` role take + * precedence over instructions given with the `user` role. Messages with the + * `assistant` role are presumed to have been generated by the model in previous + * interactions. + */ + @JvmStatic fun ofEvalItem(evalItem: EvalItem) = Input(evalItem = evalItem) } /** - * A message input to the model with a role indicating instruction following - * hierarchy. Instructions given with the `developer` or `system` role take - * precedence over instructions given with the `user` role. Messages with the - * `assistant` role are presumed to have been generated by the model in previous - * interactions. + * An interface that defines how to map each variant of [Input] to a value of type + * [T]. */ - class EvalItem + interface Visitor { + + fun visitSimpleInputMessage(simpleInputMessage: SimpleInputMessage): T + + /** + * A message input to the model with a role indicating instruction following + * hierarchy. Instructions given with the `developer` or `system` role take + * precedence over instructions given with the `user` role. Messages with the + * `assistant` role are presumed to have been generated by the model in previous + * interactions. + */ + fun visitEvalItem(evalItem: EvalItem): T + + /** + * Maps an unknown variant of [Input] to a value of type [T]. + * + * An instance of [Input] 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 Input: $json") + } + } + + internal class Deserializer : BaseDeserializer(Input::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Input { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { Input(simpleInputMessage = it, _json = json) }, + tryDeserialize(node, jacksonTypeRef())?.let { + Input(evalItem = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely + // incompatible with all the possible variants (e.g. deserializing from + // boolean). + 0 -> Input(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use + // the first completely valid match, or simply the first match if none + // are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Input::class) { + + override fun serialize( + value: Input, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.simpleInputMessage != null -> + generator.writeObject(value.simpleInputMessage) + value.evalItem != null -> generator.writeObject(value.evalItem) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Input") + } + } + } + + class SimpleInputMessage private constructor( - private val content: JsonField, - private val role: JsonField, - private val type: JsonField, + private val content: JsonField, + private val role: JsonField, private val additionalProperties: MutableMap, ) { @@ -2818,41 +2980,29 @@ private constructor( private constructor( @JsonProperty("content") @ExcludeMissing - content: JsonField = JsonMissing.of(), + content: JsonField = JsonMissing.of(), @JsonProperty("role") @ExcludeMissing - role: JsonField = JsonMissing.of(), - @JsonProperty("type") - @ExcludeMissing - type: JsonField = JsonMissing.of(), - ) : this(content, role, type, mutableMapOf()) + role: JsonField = JsonMissing.of(), + ) : this(content, role, mutableMapOf()) /** - * Text inputs to the model - can contain template strings. + * The content of the message. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * or is unexpectedly missing or null (e.g. if the server responded with an * unexpected value). */ - fun content(): Content = content.getRequired("content") + fun content(): String = content.getRequired("content") /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. + * The role of the message (e.g. "system", "assistant", "user"). * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * or is unexpectedly missing or null (e.g. if the server responded with an * unexpected value). */ - fun role(): Role = role.getRequired("role") - - /** - * The type of the message input. Always `message`. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). - */ - fun type(): Optional = type.getOptional("type") + fun role(): String = role.getRequired("role") /** * Returns the raw JSON value of [content]. @@ -2862,7 +3012,7 @@ private constructor( */ @JsonProperty("content") @ExcludeMissing - fun _content(): JsonField = content + fun _content(): JsonField = content /** * Returns the raw JSON value of [role]. @@ -2870,15 +3020,7 @@ private constructor( * Unlike [role], this method doesn't throw if the JSON field has an unexpected * type. */ - @JsonProperty("role") @ExcludeMissing fun _role(): JsonField = role - - /** - * Returns the raw JSON value of [type]. - * - * Unlike [type], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + @JsonProperty("role") @ExcludeMissing fun _role(): JsonField = role @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -2895,7 +3037,8 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [EvalItem]. + * Returns a mutable builder for constructing an instance of + * [SimpleInputMessage]. * * The following fields are required: * ```java @@ -2906,75 +3049,45 @@ private constructor( @JvmStatic fun builder() = Builder() } - /** A builder for [EvalItem]. */ + /** A builder for [SimpleInputMessage]. */ class Builder internal constructor() { - private var content: JsonField? = null - private var role: JsonField? = null - private var type: JsonField = JsonMissing.of() + private var content: JsonField? = null + private var role: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(evalItem: EvalItem) = apply { - content = evalItem.content - role = evalItem.role - type = evalItem.type - additionalProperties = evalItem.additionalProperties.toMutableMap() + internal fun from(simpleInputMessage: SimpleInputMessage) = apply { + content = simpleInputMessage.content + role = simpleInputMessage.role + additionalProperties = + simpleInputMessage.additionalProperties.toMutableMap() } - /** Text inputs to the model - can contain template strings. */ - fun content(content: Content) = content(JsonField.of(content)) + /** The content of the message. */ + fun content(content: String) = content(JsonField.of(content)) /** * Sets [Builder.content] to an arbitrary JSON value. * - * You should usually call [Builder.content] with a well-typed [Content] + * You should usually call [Builder.content] with a well-typed [String] * value instead. This method is primarily for setting the field to an * undocumented or not yet supported value. */ - fun content(content: JsonField) = apply { this.content = content } - - /** Alias for calling [content] with `Content.ofTextInput(textInput)`. */ - fun content(textInput: String) = content(Content.ofTextInput(textInput)) - - /** - * Alias for calling [content] with - * `Content.ofResponseInputText(responseInputText)`. - */ - fun content(responseInputText: ResponseInputText) = - content(Content.ofResponseInputText(responseInputText)) - - /** Alias for calling [content] with `Content.ofOutputText(outputText)`. */ - fun content(outputText: Content.OutputText) = - content(Content.ofOutputText(outputText)) + fun content(content: JsonField) = apply { this.content = content } - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - */ - fun role(role: Role) = role(JsonField.of(role)) + /** The role of the message (e.g. "system", "assistant", "user"). */ + fun role(role: String) = role(JsonField.of(role)) /** * Sets [Builder.role] to an arbitrary JSON value. * - * You should usually call [Builder.role] with a well-typed [Role] value - * instead. This method is primarily for setting the field to an - * undocumented or not yet supported value. - */ - fun role(role: JsonField) = apply { this.role = role } - - /** The type of the message input. Always `message`. */ - fun type(type: Type) = type(JsonField.of(type)) - - /** - * Sets [Builder.type] to an arbitrary JSON value. - * - * You should usually call [Builder.type] with a well-typed [Type] value + * You should usually call [Builder.role] with a well-typed [String] value * instead. This method is primarily for setting the field to an * undocumented or not yet supported value. */ - fun type(type: JsonField) = apply { this.type = type } + fun role(role: JsonField) = apply { this.role = role } fun additionalProperties(additionalProperties: Map) = apply { @@ -2999,7 +3112,7 @@ private constructor( } /** - * Returns an immutable instance of [EvalItem]. + * Returns an immutable instance of [SimpleInputMessage]. * * Further updates to this [Builder] will not mutate the returned instance. * @@ -3011,25 +3124,23 @@ private constructor( * * @throws IllegalStateException if any required field is unset. */ - fun build(): EvalItem = - EvalItem( + fun build(): SimpleInputMessage = + SimpleInputMessage( checkRequired("content", content), checkRequired("role", role), - type, additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): EvalItem = apply { + fun validate(): SimpleInputMessage = apply { if (validated) { return@apply } - content().validate() - role().validate() - type().ifPresent { it.validate() } + content() + role() validated = true } @@ -3049,159 +3160,430 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (content.asKnown().getOrNull()?.validity() ?: 0) + - (role.asKnown().getOrNull()?.validity() ?: 0) + - (type.asKnown().getOrNull()?.validity() ?: 0) + (if (content.asKnown().isPresent) 1 else 0) + + (if (role.asKnown().isPresent) 1 else 0) - /** Text inputs to the model - can contain template strings. */ - @JsonDeserialize(using = Content.Deserializer::class) - @JsonSerialize(using = Content.Serializer::class) - class Content - private constructor( - private val textInput: String? = null, - private val responseInputText: ResponseInputText? = null, - private val outputText: OutputText? = null, - private val _json: JsonValue? = null, - ) { - - /** A text input to the model. */ - fun textInput(): Optional = Optional.ofNullable(textInput) + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - /** A text input to the model. */ - fun responseInputText(): Optional = - Optional.ofNullable(responseInputText) + return /* spotless:off */ other is SimpleInputMessage && content == other.content && role == other.role && additionalProperties == other.additionalProperties /* spotless:on */ + } - /** A text output from the model. */ - fun outputText(): Optional = Optional.ofNullable(outputText) + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(content, role, additionalProperties) } + /* spotless:on */ - fun isTextInput(): Boolean = textInput != null + override fun hashCode(): Int = hashCode - fun isResponseInputText(): Boolean = responseInputText != null + override fun toString() = + "SimpleInputMessage{content=$content, role=$role, additionalProperties=$additionalProperties}" + } - fun isOutputText(): Boolean = outputText != null + /** + * A message input to the model with a role indicating instruction following + * hierarchy. Instructions given with the `developer` or `system` role take + * precedence over instructions given with the `user` role. Messages with the + * `assistant` role are presumed to have been generated by the model in previous + * interactions. + */ + class EvalItem + private constructor( + private val content: JsonField, + private val role: JsonField, + private val type: JsonField, + private val additionalProperties: MutableMap, + ) { - /** A text input to the model. */ - fun asTextInput(): String = textInput.getOrThrow("textInput") + @JsonCreator + private constructor( + @JsonProperty("content") + @ExcludeMissing + content: JsonField = JsonMissing.of(), + @JsonProperty("role") + @ExcludeMissing + role: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonField = JsonMissing.of(), + ) : this(content, role, type, mutableMapOf()) - /** A text input to the model. */ - fun asResponseInputText(): ResponseInputText = - responseInputText.getOrThrow("responseInputText") + /** + * Text inputs to the model - can contain template strings. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun content(): Content = content.getRequired("content") - /** A text output from the model. */ - fun asOutputText(): OutputText = outputText.getOrThrow("outputText") + /** + * The role of the message input. One of `user`, `assistant`, `system`, or + * `developer`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type + * or is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun role(): Role = role.getRequired("role") - fun _json(): Optional = Optional.ofNullable(_json) + /** + * The type of the message input. Always `message`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") - fun accept(visitor: Visitor): T = - when { - textInput != null -> visitor.visitTextInput(textInput) - responseInputText != null -> - visitor.visitResponseInputText(responseInputText) - outputText != null -> visitor.visitOutputText(outputText) - else -> visitor.unknown(_json) - } + /** + * Returns the raw JSON value of [content]. + * + * Unlike [content], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("content") + @ExcludeMissing + fun _content(): JsonField = content - private var validated: Boolean = false + /** + * Returns the raw JSON value of [role]. + * + * Unlike [role], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("role") @ExcludeMissing fun _role(): JsonField = role - fun validate(): Content = apply { - if (validated) { - return@apply - } + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type - accept( - object : Visitor { - override fun visitTextInput(textInput: String) {} + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } - override fun visitResponseInputText( - responseInputText: ResponseInputText - ) { - responseInputText.validate() - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - override fun visitOutputText(outputText: OutputText) { - outputText.validate() - } - } - ) - validated = true - } + fun toBuilder() = Builder().from(this) - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + companion object { /** - * Returns a score indicating how many valid values are contained in this - * object recursively. + * Returns a mutable builder for constructing an instance of [EvalItem]. * - * Used for best match union deserialization. + * The following fields are required: + * ```java + * .content() + * .role() + * ``` */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitTextInput(textInput: String) = 1 - - override fun visitResponseInputText( - responseInputText: ResponseInputText - ) = responseInputText.validity() - - override fun visitOutputText(outputText: OutputText) = - outputText.validity() + @JvmStatic fun builder() = Builder() + } - override fun unknown(json: JsonValue?) = 0 - } - ) + /** A builder for [EvalItem]. */ + class Builder internal constructor() { - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + private var content: JsonField? = null + private var role: JsonField? = null + private var type: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() - return /* spotless:off */ other is Content && textInput == other.textInput && responseInputText == other.responseInputText && outputText == other.outputText /* spotless:on */ + @JvmSynthetic + internal fun from(evalItem: EvalItem) = apply { + content = evalItem.content + role = evalItem.role + type = evalItem.type + additionalProperties = evalItem.additionalProperties.toMutableMap() } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(textInput, responseInputText, outputText) /* spotless:on */ + /** Text inputs to the model - can contain template strings. */ + fun content(content: Content) = content(JsonField.of(content)) - override fun toString(): String = - when { - textInput != null -> "Content{textInput=$textInput}" - responseInputText != null -> - "Content{responseInputText=$responseInputText}" - outputText != null -> "Content{outputText=$outputText}" - _json != null -> "Content{_unknown=$_json}" - else -> throw IllegalStateException("Invalid Content") - } + /** + * Sets [Builder.content] to an arbitrary JSON value. + * + * You should usually call [Builder.content] with a well-typed [Content] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun content(content: JsonField) = apply { this.content = content } - companion object { + /** Alias for calling [content] with `Content.ofTextInput(textInput)`. */ + fun content(textInput: String) = content(Content.ofTextInput(textInput)) - /** A text input to the model. */ - @JvmStatic - fun ofTextInput(textInput: String) = Content(textInput = textInput) + /** + * Alias for calling [content] with + * `Content.ofResponseInputText(responseInputText)`. + */ + fun content(responseInputText: ResponseInputText) = + content(Content.ofResponseInputText(responseInputText)) - /** A text input to the model. */ - @JvmStatic - fun ofResponseInputText(responseInputText: ResponseInputText) = - Content(responseInputText = responseInputText) + /** Alias for calling [content] with `Content.ofOutputText(outputText)`. */ + fun content(outputText: Content.OutputText) = + content(Content.ofOutputText(outputText)) - /** A text output from the model. */ - @JvmStatic - fun ofOutputText(outputText: OutputText) = - Content(outputText = outputText) - } + /** + * The role of the message input. One of `user`, `assistant`, `system`, or + * `developer`. + */ + fun role(role: Role) = role(JsonField.of(role)) /** - * An interface that defines how to map each variant of [Content] to a value - * of type [T]. + * Sets [Builder.role] to an arbitrary JSON value. + * + * You should usually call [Builder.role] with a well-typed [Role] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. */ - interface Visitor { + fun role(role: JsonField) = apply { this.role = role } - /** A text input to the model. */ - fun visitTextInput(textInput: String): T + /** The type of the message input. Always `message`. */ + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun type(type: JsonField) = apply { this.type = type } + + 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) + } + + /** + * Returns an immutable instance of [EvalItem]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .content() + * .role() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): EvalItem = + EvalItem( + checkRequired("content", content), + checkRequired("role", role), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): EvalItem = apply { + if (validated) { + return@apply + } + + content().validate() + role().validate() + type().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (content.asKnown().getOrNull()?.validity() ?: 0) + + (role.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + /** Text inputs to the model - can contain template strings. */ + @JsonDeserialize(using = Content.Deserializer::class) + @JsonSerialize(using = Content.Serializer::class) + class Content + private constructor( + private val textInput: String? = null, + private val responseInputText: ResponseInputText? = null, + private val outputText: OutputText? = null, + private val _json: JsonValue? = null, + ) { + + /** A text input to the model. */ + fun textInput(): Optional = Optional.ofNullable(textInput) + + /** A text input to the model. */ + fun responseInputText(): Optional = + Optional.ofNullable(responseInputText) + + /** A text output from the model. */ + fun outputText(): Optional = Optional.ofNullable(outputText) + + fun isTextInput(): Boolean = textInput != null + + fun isResponseInputText(): Boolean = responseInputText != null + + fun isOutputText(): Boolean = outputText != null + + /** A text input to the model. */ + fun asTextInput(): String = textInput.getOrThrow("textInput") + + /** A text input to the model. */ + fun asResponseInputText(): ResponseInputText = + responseInputText.getOrThrow("responseInputText") + + /** A text output from the model. */ + fun asOutputText(): OutputText = outputText.getOrThrow("outputText") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + textInput != null -> visitor.visitTextInput(textInput) + responseInputText != null -> + visitor.visitResponseInputText(responseInputText) + outputText != null -> visitor.visitOutputText(outputText) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Content = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitTextInput(textInput: String) {} + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) { + responseInputText.validate() + } + + override fun visitOutputText(outputText: OutputText) { + outputText.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this + * object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitTextInput(textInput: String) = 1 + + override fun visitResponseInputText( + responseInputText: ResponseInputText + ) = responseInputText.validity() + + override fun visitOutputText(outputText: OutputText) = + outputText.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Content && textInput == other.textInput && responseInputText == other.responseInputText && outputText == other.outputText /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(textInput, responseInputText, outputText) /* spotless:on */ + + override fun toString(): String = + when { + textInput != null -> "Content{textInput=$textInput}" + responseInputText != null -> + "Content{responseInputText=$responseInputText}" + outputText != null -> "Content{outputText=$outputText}" + _json != null -> "Content{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Content") + } + + companion object { + + /** A text input to the model. */ + @JvmStatic + fun ofTextInput(textInput: String) = Content(textInput = textInput) + + /** A text input to the model. */ + @JvmStatic + fun ofResponseInputText(responseInputText: ResponseInputText) = + Content(responseInputText = responseInputText) + + /** A text output from the model. */ + @JvmStatic + fun ofOutputText(outputText: OutputText) = + Content(outputText = outputText) + } + + /** + * An interface that defines how to map each variant of [Content] to a value + * of type [T]. + */ + interface Visitor { + + /** A text input to the model. */ + fun visitTextInput(textInput: String): T /** A text input to the model. */ fun visitResponseInputText(responseInputText: ResponseInputText): T @@ -3745,1779 +4127,1210 @@ private constructor( fun validate(): Type = apply { if (validated) { - return@apply - } - - known() - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this - * object recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is Type && 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 EvalItem && content == other.content && role == other.role && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ - } - - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(content, role, type, additionalProperties) } - /* spotless:on */ - - override fun hashCode(): Int = hashCode - - override fun toString() = - "EvalItem{content=$content, role=$role, type=$type, additionalProperties=$additionalProperties}" - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is LabelModel && input == other.input && labels == other.labels && model == other.model && name == other.name && passingLabels == other.passingLabels && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ - } - - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(input, labels, model, name, passingLabels, type, additionalProperties) } - /* spotless:on */ - - override fun hashCode(): Int = hashCode - - override fun toString() = - "LabelModel{input=$input, labels=$labels, model=$model, name=$name, passingLabels=$passingLabels, type=$type, additionalProperties=$additionalProperties}" - } - - /** A PythonGrader object that runs a python script on the input. */ - class Python - private constructor( - private val name: JsonField, - private val source: JsonField, - private val type: JsonValue, - private val imageTag: JsonField, - private val passThreshold: JsonField, - private val additionalProperties: MutableMap, - ) { - - @JsonCreator - private constructor( - @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), - @JsonProperty("source") - @ExcludeMissing - source: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - @JsonProperty("image_tag") - @ExcludeMissing - imageTag: JsonField = JsonMissing.of(), - @JsonProperty("pass_threshold") - @ExcludeMissing - passThreshold: JsonField = JsonMissing.of(), - ) : this(name, source, type, imageTag, passThreshold, mutableMapOf()) - - /** - * The name of the grader. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun name(): String = name.getRequired("name") - - /** - * The source code of the python script. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun source(): String = source.getRequired("source") - - /** - * The object type, which is always `python`. - * - * Expected to always return the following: - * ```java - * JsonValue.from("python") - * ``` - * - * However, this method can be useful for debugging and logging (e.g. if the server - * responded with an unexpected value). - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - - /** - * The image tag to use for the python script. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun imageTag(): Optional = imageTag.getOptional("image_tag") - - /** - * The threshold for the score. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") - - /** - * Returns the raw JSON value of [name]. - * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name - - /** - * Returns the raw JSON value of [source]. - * - * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source - - /** - * Returns the raw JSON value of [imageTag]. - * - * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag - - /** - * Returns the raw JSON value of [passThreshold]. - * - * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("pass_threshold") - @ExcludeMissing - fun _passThreshold(): JsonField = passThreshold - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { - - /** - * Returns a mutable builder for constructing an instance of [Python]. - * - * The following fields are required: - * ```java - * .name() - * .source() - * ``` - */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [Python]. */ - class Builder internal constructor() { - - private var name: JsonField? = null - private var source: JsonField? = null - private var type: JsonValue = JsonValue.from("python") - private var imageTag: JsonField = JsonMissing.of() - private var passThreshold: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(python: Python) = apply { - name = python.name - source = python.source - type = python.type - imageTag = python.imageTag - passThreshold = python.passThreshold - additionalProperties = python.additionalProperties.toMutableMap() - } - - /** The name of the grader. */ - fun name(name: String) = name(JsonField.of(name)) - - /** - * Sets [Builder.name] to an arbitrary JSON value. - * - * You should usually call [Builder.name] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun name(name: JsonField) = apply { this.name = name } - - /** The source code of the python script. */ - fun source(source: String) = source(JsonField.of(source)) - - /** - * Sets [Builder.source] to an arbitrary JSON value. - * - * You should usually call [Builder.source] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun source(source: JsonField) = apply { this.source = source } - - /** - * Sets the field to an arbitrary JSON value. - * - * It is usually unnecessary to call this method because the field defaults to the - * following: - * ```java - * JsonValue.from("python") - * ``` - * - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun type(type: JsonValue) = apply { this.type = type } - - /** The image tag to use for the python script. */ - fun imageTag(imageTag: String) = imageTag(JsonField.of(imageTag)) - - /** - * Sets [Builder.imageTag] to an arbitrary JSON value. - * - * You should usually call [Builder.imageTag] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun imageTag(imageTag: JsonField) = apply { this.imageTag = imageTag } - - /** The threshold for the score. */ - fun passThreshold(passThreshold: Double) = - passThreshold(JsonField.of(passThreshold)) - - /** - * Sets [Builder.passThreshold] to an arbitrary JSON value. - * - * You should usually call [Builder.passThreshold] with a well-typed [Double] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun passThreshold(passThreshold: JsonField) = apply { - this.passThreshold = passThreshold - } - - 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) - } - - /** - * Returns an immutable instance of [Python]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .name() - * .source() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): Python = - Python( - checkRequired("name", name), - checkRequired("source", source), - type, - imageTag, - passThreshold, - additionalProperties.toMutableMap(), - ) - } - - private var validated: Boolean = false - - fun validate(): Python = apply { - if (validated) { - return@apply - } - - name() - source() - _type().let { - if (it != JsonValue.from("python")) { - throw OpenAIInvalidDataException("'type' is invalid, received $it") - } - } - imageTag() - passThreshold() - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (if (name.asKnown().isPresent) 1 else 0) + - (if (source.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("python")) 1 else 0 } + - (if (imageTag.asKnown().isPresent) 1 else 0) + - (if (passThreshold.asKnown().isPresent) 1 else 0) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is Python && name == other.name && source == other.source && type == other.type && imageTag == other.imageTag && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ - } - - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, additionalProperties) } - /* spotless:on */ - - override fun hashCode(): Int = hashCode - - override fun toString() = - "Python{name=$name, source=$source, type=$type, imageTag=$imageTag, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" - } - - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - class ScoreModel - private constructor( - private val input: JsonField>, - private val model: JsonField, - private val name: JsonField, - private val type: JsonValue, - private val passThreshold: JsonField, - private val range: JsonField>, - private val samplingParams: JsonValue, - private val additionalProperties: MutableMap, - ) { - - @JsonCreator - private constructor( - @JsonProperty("input") - @ExcludeMissing - input: JsonField> = JsonMissing.of(), - @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), - @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - @JsonProperty("pass_threshold") - @ExcludeMissing - passThreshold: JsonField = JsonMissing.of(), - @JsonProperty("range") - @ExcludeMissing - range: JsonField> = JsonMissing.of(), - @JsonProperty("sampling_params") - @ExcludeMissing - samplingParams: JsonValue = JsonMissing.of(), - ) : this(input, model, name, type, passThreshold, range, samplingParams, mutableMapOf()) - - /** - * The input text. This may include template strings. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun input(): List = input.getRequired("input") - - /** - * The model to use for the evaluation. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun model(): String = model.getRequired("model") - - /** - * The name of the grader. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun name(): String = name.getRequired("name") - - /** - * The object type, which is always `score_model`. - * - * Expected to always return the following: - * ```java - * JsonValue.from("score_model") - * ``` - * - * However, this method can be useful for debugging and logging (e.g. if the server - * responded with an unexpected value). - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - - /** - * The threshold for the score. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") - - /** - * The range of the score. Defaults to `[0, 1]`. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun range(): Optional> = range.getOptional("range") - - /** The sampling parameters for the model. */ - @JsonProperty("sampling_params") - @ExcludeMissing - fun _samplingParams(): JsonValue = samplingParams - - /** - * Returns the raw JSON value of [input]. - * - * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("input") @ExcludeMissing fun _input(): JsonField> = input - - /** - * Returns the raw JSON value of [model]. - * - * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model - - /** - * Returns the raw JSON value of [name]. - * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name - - /** - * Returns the raw JSON value of [passThreshold]. - * - * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("pass_threshold") - @ExcludeMissing - fun _passThreshold(): JsonField = passThreshold - - /** - * Returns the raw JSON value of [range]. - * - * Unlike [range], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("range") @ExcludeMissing fun _range(): JsonField> = range - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { - - /** - * Returns a mutable builder for constructing an instance of [ScoreModel]. - * - * The following fields are required: - * ```java - * .input() - * .model() - * .name() - * ``` - */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [ScoreModel]. */ - class Builder internal constructor() { - - private var input: JsonField>? = null - private var model: JsonField? = null - private var name: JsonField? = null - private var type: JsonValue = JsonValue.from("score_model") - private var passThreshold: JsonField = JsonMissing.of() - private var range: JsonField>? = null - private var samplingParams: JsonValue = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(scoreModel: ScoreModel) = apply { - input = scoreModel.input.map { it.toMutableList() } - model = scoreModel.model - name = scoreModel.name - type = scoreModel.type - passThreshold = scoreModel.passThreshold - range = scoreModel.range.map { it.toMutableList() } - samplingParams = scoreModel.samplingParams - additionalProperties = scoreModel.additionalProperties.toMutableMap() - } - - /** The input text. This may include template strings. */ - fun input(input: List) = input(JsonField.of(input)) - - /** - * Sets [Builder.input] to an arbitrary JSON value. - * - * You should usually call [Builder.input] with a well-typed `List` value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun input(input: JsonField>) = apply { - this.input = input.map { it.toMutableList() } - } - - /** - * Adds a single [Input] to [Builder.input]. - * - * @throws IllegalStateException if the field was previously set to a non-list. - */ - fun addInput(input: Input) = apply { - this.input = - (this.input ?: JsonField.of(mutableListOf())).also { - checkKnown("input", it).add(input) - } - } - - /** The model to use for the evaluation. */ - fun model(model: String) = model(JsonField.of(model)) - - /** - * Sets [Builder.model] to an arbitrary JSON value. - * - * You should usually call [Builder.model] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun model(model: JsonField) = apply { this.model = model } - - /** The name of the grader. */ - fun name(name: String) = name(JsonField.of(name)) - - /** - * Sets [Builder.name] to an arbitrary JSON value. - * - * You should usually call [Builder.name] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun name(name: JsonField) = apply { this.name = name } - - /** - * Sets the field to an arbitrary JSON value. - * - * It is usually unnecessary to call this method because the field defaults to the - * following: - * ```java - * JsonValue.from("score_model") - * ``` - * - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun type(type: JsonValue) = apply { this.type = type } - - /** The threshold for the score. */ - fun passThreshold(passThreshold: Double) = - passThreshold(JsonField.of(passThreshold)) - - /** - * Sets [Builder.passThreshold] to an arbitrary JSON value. - * - * You should usually call [Builder.passThreshold] with a well-typed [Double] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun passThreshold(passThreshold: JsonField) = apply { - this.passThreshold = passThreshold - } - - /** The range of the score. Defaults to `[0, 1]`. */ - fun range(range: List) = range(JsonField.of(range)) - - /** - * Sets [Builder.range] to an arbitrary JSON value. - * - * You should usually call [Builder.range] with a well-typed `List` value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun range(range: JsonField>) = apply { - this.range = range.map { it.toMutableList() } - } - - /** - * Adds a single [Double] to [Builder.range]. - * - * @throws IllegalStateException if the field was previously set to a non-list. - */ - fun addRange(range: Double) = apply { - this.range = - (this.range ?: JsonField.of(mutableListOf())).also { - checkKnown("range", it).add(range) - } - } - - /** The sampling parameters for the model. */ - fun samplingParams(samplingParams: JsonValue) = apply { - this.samplingParams = samplingParams - } - - 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) - } - - /** - * Returns an immutable instance of [ScoreModel]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .input() - * .model() - * .name() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): ScoreModel = - ScoreModel( - checkRequired("input", input).map { it.toImmutable() }, - checkRequired("model", model), - checkRequired("name", name), - type, - passThreshold, - (range ?: JsonMissing.of()).map { it.toImmutable() }, - samplingParams, - additionalProperties.toMutableMap(), - ) - } - - private var validated: Boolean = false - - fun validate(): ScoreModel = apply { - if (validated) { - return@apply - } + return@apply + } - input().forEach { it.validate() } - model() - name() - _type().let { - if (it != JsonValue.from("score_model")) { - throw OpenAIInvalidDataException("'type' is invalid, received $it") - } - } - passThreshold() - range() - validated = true - } + known() + validated = true + } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (input.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + - (if (model.asKnown().isPresent) 1 else 0) + - (if (name.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("score_model")) 1 else 0 } + - (if (passThreshold.asKnown().isPresent) 1 else 0) + - (range.asKnown().getOrNull()?.size ?: 0) + /** + * Returns a score indicating how many valid values are contained in this + * object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 - /** - * A message input to the model with a role indicating instruction following hierarchy. - * Instructions given with the `developer` or `system` role take precedence over - * instructions given with the `user` role. Messages with the `assistant` role are - * presumed to have been generated by the model in previous interactions. - */ - class Input - private constructor( - private val content: JsonField, - private val role: JsonField, - private val type: JsonField, - private val additionalProperties: MutableMap, - ) { + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - @JsonCreator - private constructor( - @JsonProperty("content") - @ExcludeMissing - content: JsonField = JsonMissing.of(), - @JsonProperty("role") @ExcludeMissing role: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), - ) : this(content, role, type, mutableMapOf()) + return /* spotless:off */ other is Type && value == other.value /* spotless:on */ + } - /** - * Text inputs to the model - can contain template strings. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun content(): Content = content.getRequired("content") + override fun hashCode() = value.hashCode() - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun role(): Role = role.getRequired("role") + override fun toString() = value.toString() + } - /** - * The type of the message input. Always `message`. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun type(): Optional = type.getOptional("type") + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - /** - * Returns the raw JSON value of [content]. - * - * Unlike [content], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("content") - @ExcludeMissing - fun _content(): JsonField = content + return /* spotless:off */ other is EvalItem && content == other.content && role == other.role && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ + } - /** - * Returns the raw JSON value of [role]. - * - * Unlike [role], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("role") @ExcludeMissing fun _role(): JsonField = role + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(content, role, type, additionalProperties) } + /* spotless:on */ - /** - * Returns the raw JSON value of [type]. - * - * Unlike [type], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + override fun hashCode(): Int = hashCode - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) + override fun toString() = + "EvalItem{content=$content, role=$role, type=$type, additionalProperties=$additionalProperties}" } + } - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - fun toBuilder() = Builder().from(this) + return /* spotless:off */ other is LabelModel && input == other.input && labels == other.labels && model == other.model && name == other.name && passingLabels == other.passingLabels && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ + } - companion object { + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(input, labels, model, name, passingLabels, type, additionalProperties) } + /* spotless:on */ - /** - * Returns a mutable builder for constructing an instance of [Input]. - * - * The following fields are required: - * ```java - * .content() - * .role() - * ``` - */ - @JvmStatic fun builder() = Builder() - } + override fun hashCode(): Int = hashCode - /** A builder for [Input]. */ - class Builder internal constructor() { + override fun toString() = + "LabelModel{input=$input, labels=$labels, model=$model, name=$name, passingLabels=$passingLabels, type=$type, additionalProperties=$additionalProperties}" + } - private var content: JsonField? = null - private var role: JsonField? = null - private var type: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + class TextSimilarity + private constructor( + private val evaluationMetric: JsonField, + private val input: JsonField, + private val name: JsonField, + private val reference: JsonField, + private val type: JsonValue, + private val passThreshold: JsonField, + private val additionalProperties: MutableMap, + ) { - @JvmSynthetic - internal fun from(input: Input) = apply { - content = input.content - role = input.role - type = input.type - additionalProperties = input.additionalProperties.toMutableMap() - } + @JsonCreator + private constructor( + @JsonProperty("evaluation_metric") + @ExcludeMissing + evaluationMetric: JsonField = + JsonMissing.of(), + @JsonProperty("input") @ExcludeMissing input: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("reference") + @ExcludeMissing + reference: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("pass_threshold") + @ExcludeMissing + passThreshold: JsonField = JsonMissing.of(), + ) : this(evaluationMetric, input, name, reference, type, passThreshold, mutableMapOf()) - /** Text inputs to the model - can contain template strings. */ - fun content(content: Content) = content(JsonField.of(content)) + fun toTextSimilarityGrader(): TextSimilarityGrader = + TextSimilarityGrader.builder() + .evaluationMetric(evaluationMetric) + .input(input) + .name(name) + .reference(reference) + .type(type) + .build() - /** - * Sets [Builder.content] to an arbitrary JSON value. - * - * You should usually call [Builder.content] with a well-typed [Content] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun content(content: JsonField) = apply { this.content = content } + /** + * The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, + * `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun evaluationMetric(): TextSimilarityGrader.EvaluationMetric = + evaluationMetric.getRequired("evaluation_metric") - /** Alias for calling [content] with `Content.ofTextInput(textInput)`. */ - fun content(textInput: String) = content(Content.ofTextInput(textInput)) + /** + * The text being graded. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun input(): String = input.getRequired("input") - /** - * Alias for calling [content] with - * `Content.ofResponseInputText(responseInputText)`. - */ - fun content(responseInputText: ResponseInputText) = - content(Content.ofResponseInputText(responseInputText)) + /** + * The name of the grader. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun name(): String = name.getRequired("name") - /** Alias for calling [content] with `Content.ofOutputText(outputText)`. */ - fun content(outputText: Content.OutputText) = - content(Content.ofOutputText(outputText)) + /** + * The text being graded against. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun reference(): String = reference.getRequired("reference") - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - */ - fun role(role: Role) = role(JsonField.of(role)) + /** + * The type of grader. + * + * Expected to always return the following: + * ```java + * JsonValue.from("text_similarity") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - /** - * Sets [Builder.role] to an arbitrary JSON value. - * - * You should usually call [Builder.role] with a well-typed [Role] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun role(role: JsonField) = apply { this.role = role } + /** + * The threshold for the score. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun passThreshold(): Double = passThreshold.getRequired("pass_threshold") - /** The type of the message input. Always `message`. */ - fun type(type: Type) = type(JsonField.of(type)) + /** + * Returns the raw JSON value of [evaluationMetric]. + * + * Unlike [evaluationMetric], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("evaluation_metric") + @ExcludeMissing + fun _evaluationMetric(): JsonField = + evaluationMetric - /** - * Sets [Builder.type] to an arbitrary JSON value. - * - * You should usually call [Builder.type] with a well-typed [Type] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun type(type: JsonField) = apply { this.type = type } + /** + * Returns the raw JSON value of [input]. + * + * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("input") @ExcludeMissing fun _input(): JsonField = input - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + /** + * Returns the raw JSON value of [reference]. + * + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("reference") + @ExcludeMissing + fun _reference(): JsonField = reference - fun putAllAdditionalProperties(additionalProperties: Map) = - apply { - this.additionalProperties.putAll(additionalProperties) - } + /** + * Returns the raw JSON value of [passThreshold]. + * + * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pass_threshold") + @ExcludeMissing + fun _passThreshold(): JsonField = passThreshold - fun removeAdditionalProperty(key: String) = apply { - additionalProperties.remove(key) - } + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + fun toBuilder() = Builder().from(this) - /** - * Returns an immutable instance of [Input]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .content() - * .role() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): Input = - Input( - checkRequired("content", content), - checkRequired("role", role), - type, - additionalProperties.toMutableMap(), - ) - } + companion object { - private var validated: Boolean = false + /** + * Returns a mutable builder for constructing an instance of [TextSimilarity]. + * + * The following fields are required: + * ```java + * .evaluationMetric() + * .input() + * .name() + * .reference() + * .passThreshold() + * ``` + */ + @JvmStatic fun builder() = Builder() + } - fun validate(): Input = apply { - if (validated) { - return@apply - } + /** A builder for [TextSimilarity]. */ + class Builder internal constructor() { - content().validate() - role().validate() - type().ifPresent { it.validate() } - validated = true + private var evaluationMetric: JsonField? = + null + private var input: JsonField? = null + private var name: JsonField? = null + private var reference: JsonField? = null + private var type: JsonValue = JsonValue.from("text_similarity") + private var passThreshold: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(textSimilarity: TextSimilarity) = apply { + evaluationMetric = textSimilarity.evaluationMetric + input = textSimilarity.input + name = textSimilarity.name + reference = textSimilarity.reference + type = textSimilarity.type + passThreshold = textSimilarity.passThreshold + additionalProperties = textSimilarity.additionalProperties.toMutableMap() } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + /** + * The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, + * `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. + */ + fun evaluationMetric(evaluationMetric: TextSimilarityGrader.EvaluationMetric) = + evaluationMetric(JsonField.of(evaluationMetric)) /** - * Returns a score indicating how many valid values are contained in this object - * recursively. + * Sets [Builder.evaluationMetric] to an arbitrary JSON value. * - * Used for best match union deserialization. + * You should usually call [Builder.evaluationMetric] with a well-typed + * [TextSimilarityGrader.EvaluationMetric] value instead. This method is primarily + * for setting the field to an undocumented or not yet supported value. */ - @JvmSynthetic - internal fun validity(): Int = - (content.asKnown().getOrNull()?.validity() ?: 0) + - (role.asKnown().getOrNull()?.validity() ?: 0) + - (type.asKnown().getOrNull()?.validity() ?: 0) - - /** Text inputs to the model - can contain template strings. */ - @JsonDeserialize(using = Content.Deserializer::class) - @JsonSerialize(using = Content.Serializer::class) - class Content - private constructor( - private val textInput: String? = null, - private val responseInputText: ResponseInputText? = null, - private val outputText: OutputText? = null, - private val _json: JsonValue? = null, - ) { + fun evaluationMetric( + evaluationMetric: JsonField + ) = apply { this.evaluationMetric = evaluationMetric } - /** A text input to the model. */ - fun textInput(): Optional = Optional.ofNullable(textInput) + /** The text being graded. */ + fun input(input: String) = input(JsonField.of(input)) - /** A text input to the model. */ - fun responseInputText(): Optional = - Optional.ofNullable(responseInputText) + /** + * Sets [Builder.input] to an arbitrary JSON value. + * + * You should usually call [Builder.input] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun input(input: JsonField) = apply { this.input = input } - /** A text output from the model. */ - fun outputText(): Optional = Optional.ofNullable(outputText) + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) - fun isTextInput(): Boolean = textInput != null + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun name(name: JsonField) = apply { this.name = name } - fun isResponseInputText(): Boolean = responseInputText != null + /** The text being graded against. */ + fun reference(reference: String) = reference(JsonField.of(reference)) - fun isOutputText(): Boolean = outputText != null + /** + * Sets [Builder.reference] to an arbitrary JSON value. + * + * You should usually call [Builder.reference] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun reference(reference: JsonField) = apply { this.reference = reference } - /** A text input to the model. */ - fun asTextInput(): String = textInput.getOrThrow("textInput") + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("text_similarity") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - /** A text input to the model. */ - fun asResponseInputText(): ResponseInputText = - responseInputText.getOrThrow("responseInputText") + /** The threshold for the score. */ + fun passThreshold(passThreshold: Double) = + passThreshold(JsonField.of(passThreshold)) - /** A text output from the model. */ - fun asOutputText(): OutputText = outputText.getOrThrow("outputText") + /** + * Sets [Builder.passThreshold] to an arbitrary JSON value. + * + * You should usually call [Builder.passThreshold] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun passThreshold(passThreshold: JsonField) = apply { + this.passThreshold = passThreshold + } - fun _json(): Optional = Optional.ofNullable(_json) + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - fun accept(visitor: Visitor): T = - when { - textInput != null -> visitor.visitTextInput(textInput) - responseInputText != null -> - visitor.visitResponseInputText(responseInputText) - outputText != null -> visitor.visitOutputText(outputText) - else -> visitor.unknown(_json) - } + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - private var validated: Boolean = false + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } - fun validate(): Content = apply { - if (validated) { - return@apply - } + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - accept( - object : Visitor { - override fun visitTextInput(textInput: String) {} + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - override fun visitResponseInputText( - responseInputText: ResponseInputText - ) { - responseInputText.validate() - } + /** + * Returns an immutable instance of [TextSimilarity]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .evaluationMetric() + * .input() + * .name() + * .reference() + * .passThreshold() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): TextSimilarity = + TextSimilarity( + checkRequired("evaluationMetric", evaluationMetric), + checkRequired("input", input), + checkRequired("name", name), + checkRequired("reference", reference), + type, + checkRequired("passThreshold", passThreshold), + additionalProperties.toMutableMap(), + ) + } - override fun visitOutputText(outputText: OutputText) { - outputText.validate() - } - } - ) - validated = true + private var validated: Boolean = false + + fun validate(): TextSimilarity = apply { + if (validated) { + return@apply + } + + evaluationMetric().validate() + input() + name() + reference() + _type().let { + if (it != JsonValue.from("text_similarity")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") } + } + passThreshold() + validated = true + } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitTextInput(textInput: String) = 1 + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (evaluationMetric.asKnown().getOrNull()?.validity() ?: 0) + + (if (input.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (reference.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("text_similarity")) 1 else 0 } + + (if (passThreshold.asKnown().isPresent) 1 else 0) - override fun visitResponseInputText( - responseInputText: ResponseInputText - ) = responseInputText.validity() + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - override fun visitOutputText(outputText: OutputText) = - outputText.validity() + return /* spotless:off */ other is TextSimilarity && evaluationMetric == other.evaluationMetric && input == other.input && name == other.name && reference == other.reference && type == other.type && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ + } - override fun unknown(json: JsonValue?) = 0 - } - ) + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(evaluationMetric, input, name, reference, type, passThreshold, additionalProperties) } + /* spotless:on */ - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + override fun hashCode(): Int = hashCode - return /* spotless:off */ other is Content && textInput == other.textInput && responseInputText == other.responseInputText && outputText == other.outputText /* spotless:on */ - } + override fun toString() = + "TextSimilarity{evaluationMetric=$evaluationMetric, input=$input, name=$name, reference=$reference, type=$type, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" + } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(textInput, responseInputText, outputText) /* spotless:on */ + /** A PythonGrader object that runs a python script on the input. */ + class Python + private constructor( + private val name: JsonField, + private val source: JsonField, + private val type: JsonValue, + private val imageTag: JsonField, + private val passThreshold: JsonField, + private val additionalProperties: MutableMap, + ) { - override fun toString(): String = - when { - textInput != null -> "Content{textInput=$textInput}" - responseInputText != null -> - "Content{responseInputText=$responseInputText}" - outputText != null -> "Content{outputText=$outputText}" - _json != null -> "Content{_unknown=$_json}" - else -> throw IllegalStateException("Invalid Content") - } + @JsonCreator + private constructor( + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("source") + @ExcludeMissing + source: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("image_tag") + @ExcludeMissing + imageTag: JsonField = JsonMissing.of(), + @JsonProperty("pass_threshold") + @ExcludeMissing + passThreshold: JsonField = JsonMissing.of(), + ) : this(name, source, type, imageTag, passThreshold, mutableMapOf()) - companion object { + fun toPythonGrader(): PythonGrader = + PythonGrader.builder() + .name(name) + .source(source) + .type(type) + .imageTag(imageTag) + .build() - /** A text input to the model. */ - @JvmStatic - fun ofTextInput(textInput: String) = Content(textInput = textInput) + /** + * The name of the grader. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun name(): String = name.getRequired("name") - /** A text input to the model. */ - @JvmStatic - fun ofResponseInputText(responseInputText: ResponseInputText) = - Content(responseInputText = responseInputText) + /** + * The source code of the python script. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun source(): String = source.getRequired("source") - /** A text output from the model. */ - @JvmStatic - fun ofOutputText(outputText: OutputText) = Content(outputText = outputText) - } + /** + * The object type, which is always `python`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("python") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - /** - * An interface that defines how to map each variant of [Content] to a value of - * type [T]. - */ - interface Visitor { + /** + * The image tag to use for the python script. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun imageTag(): Optional = imageTag.getOptional("image_tag") - /** A text input to the model. */ - fun visitTextInput(textInput: String): T + /** + * The threshold for the score. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") - /** A text input to the model. */ - fun visitResponseInputText(responseInputText: ResponseInputText): T + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name - /** A text output from the model. */ - fun visitOutputText(outputText: OutputText): T + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source - /** - * Maps an unknown variant of [Content] to a value of type [T]. - * - * An instance of [Content] 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 Content: $json") - } - } + /** + * Returns the raw JSON value of [imageTag]. + * + * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag - internal class Deserializer : BaseDeserializer(Content::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): Content { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { Content(responseInputText = it, _json = json) }, - tryDeserialize(node, jacksonTypeRef())?.let { - Content(outputText = it, _json = json) - }, - tryDeserialize(node, jacksonTypeRef())?.let { - Content(textInput = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from array). - 0 -> Content(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } - } + /** + * Returns the raw JSON value of [passThreshold]. + * + * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pass_threshold") + @ExcludeMissing + fun _passThreshold(): JsonField = passThreshold - internal class Serializer : BaseSerializer(Content::class) { + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } - override fun serialize( - value: Content, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.textInput != null -> generator.writeObject(value.textInput) - value.responseInputText != null -> - generator.writeObject(value.responseInputText) - value.outputText != null -> generator.writeObject(value.outputText) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid Content") - } - } - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - /** A text output from the model. */ - class OutputText - private constructor( - private val text: JsonField, - private val type: JsonValue, - private val additionalProperties: MutableMap, - ) { + fun toBuilder() = Builder().from(this) - @JsonCreator - private constructor( - @JsonProperty("text") - @ExcludeMissing - text: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - ) : this(text, type, mutableMapOf()) + companion object { - /** - * The text output from the model. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected - * type or is unexpectedly missing or null (e.g. if the server responded - * with an unexpected value). - */ - fun text(): String = text.getRequired("text") + /** + * Returns a mutable builder for constructing an instance of [Python]. + * + * The following fields are required: + * ```java + * .name() + * .source() + * ``` + */ + @JvmStatic fun builder() = Builder() + } - /** - * The type of the output text. Always `output_text`. - * - * Expected to always return the following: - * ```java - * JsonValue.from("output_text") - * ``` - * - * However, this method can be useful for debugging and logging (e.g. if the - * server responded with an unexpected value). - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + /** A builder for [Python]. */ + class Builder internal constructor() { - /** - * Returns the raw JSON value of [text]. - * - * Unlike [text], this method doesn't throw if the JSON field has an - * unexpected type. - */ - @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text + private var name: JsonField? = null + private var source: JsonField? = null + private var type: JsonValue = JsonValue.from("python") + private var imageTag: JsonField = JsonMissing.of() + private var passThreshold: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } + @JvmSynthetic + internal fun from(python: Python) = apply { + name = python.name + source = python.source + type = python.type + imageTag = python.imageTag + passThreshold = python.passThreshold + additionalProperties = python.additionalProperties.toMutableMap() + } - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) - fun toBuilder() = Builder().from(this) + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun name(name: JsonField) = apply { this.name = name } - companion object { + /** The source code of the python script. */ + fun source(source: String) = source(JsonField.of(source)) - /** - * Returns a mutable builder for constructing an instance of - * [OutputText]. - * - * The following fields are required: - * ```java - * .text() - * ``` - */ - @JvmStatic fun builder() = Builder() - } + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun source(source: JsonField) = apply { this.source = source } - /** A builder for [OutputText]. */ - class Builder internal constructor() { + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("python") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - private var text: JsonField? = null - private var type: JsonValue = JsonValue.from("output_text") - private var additionalProperties: MutableMap = - mutableMapOf() + /** The image tag to use for the python script. */ + fun imageTag(imageTag: String) = imageTag(JsonField.of(imageTag)) - @JvmSynthetic - internal fun from(outputText: OutputText) = apply { - text = outputText.text - type = outputText.type - additionalProperties = - outputText.additionalProperties.toMutableMap() - } + /** + * Sets [Builder.imageTag] to an arbitrary JSON value. + * + * You should usually call [Builder.imageTag] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun imageTag(imageTag: JsonField) = apply { this.imageTag = imageTag } - /** The text output from the model. */ - fun text(text: String) = text(JsonField.of(text)) + /** The threshold for the score. */ + fun passThreshold(passThreshold: Double) = + passThreshold(JsonField.of(passThreshold)) - /** - * Sets [Builder.text] to an arbitrary JSON value. - * - * You should usually call [Builder.text] with a well-typed [String] - * value instead. This method is primarily for setting the field to an - * undocumented or not yet supported value. - */ - fun text(text: JsonField) = apply { this.text = text } + /** + * Sets [Builder.passThreshold] to an arbitrary JSON value. + * + * You should usually call [Builder.passThreshold] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun passThreshold(passThreshold: JsonField) = apply { + this.passThreshold = passThreshold + } - /** - * Sets the field to an arbitrary JSON value. - * - * It is usually unnecessary to call this method because the field - * defaults to the following: - * ```java - * JsonValue.from("output_text") - * ``` - * - * This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun type(type: JsonValue) = apply { this.type = type } + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - fun additionalProperties(additionalProperties: Map) = - apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } - fun putAllAdditionalProperties( - additionalProperties: Map - ) = apply { this.additionalProperties.putAll(additionalProperties) } + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - fun removeAdditionalProperty(key: String) = apply { - additionalProperties.remove(key) - } + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** + * Returns an immutable instance of [Python]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .name() + * .source() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Python = + Python( + checkRequired("name", name), + checkRequired("source", source), + type, + imageTag, + passThreshold, + additionalProperties.toMutableMap(), + ) + } - /** - * Returns an immutable instance of [OutputText]. - * - * Further updates to this [Builder] will not mutate the returned - * instance. - * - * The following fields are required: - * ```java - * .text() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): OutputText = - OutputText( - checkRequired("text", text), - type, - additionalProperties.toMutableMap(), - ) - } + private var validated: Boolean = false - private var validated: Boolean = false + fun validate(): Python = apply { + if (validated) { + return@apply + } - fun validate(): OutputText = apply { - if (validated) { - return@apply - } + name() + source() + _type().let { + if (it != JsonValue.from("python")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + imageTag() + passThreshold() + validated = true + } - text() - _type().let { - if (it != JsonValue.from("output_text")) { - throw OpenAIInvalidDataException( - "'type' is invalid, received $it" - ) - } - } - validated = true - } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (name.asKnown().isPresent) 1 else 0) + + (if (source.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("python")) 1 else 0 } + + (if (imageTag.asKnown().isPresent) 1 else 0) + + (if (passThreshold.asKnown().isPresent) 1 else 0) - /** - * Returns a score indicating how many valid values are contained in this - * object recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (if (text.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("output_text")) 1 else 0 } + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + return /* spotless:off */ other is Python && name == other.name && source == other.source && type == other.type && imageTag == other.imageTag && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ + } - return /* spotless:off */ other is OutputText && text == other.text && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ - } + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, additionalProperties) } + /* spotless:on */ - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(text, type, additionalProperties) } - /* spotless:on */ + override fun hashCode(): Int = hashCode - override fun hashCode(): Int = hashCode + override fun toString() = + "Python{name=$name, source=$source, type=$type, imageTag=$imageTag, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" + } - override fun toString() = - "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" - } - } + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + class ScoreModel + private constructor( + private val input: JsonField>, + private val model: JsonField, + private val name: JsonField, + private val type: JsonValue, + private val range: JsonField>, + private val samplingParams: JsonValue, + private val passThreshold: JsonField, + private val additionalProperties: MutableMap, + ) { - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - */ - class Role @JsonCreator private constructor(private val value: JsonField) : - Enum { + @JsonCreator + private constructor( + @JsonProperty("input") + @ExcludeMissing + input: JsonField> = JsonMissing.of(), + @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("range") + @ExcludeMissing + range: JsonField> = JsonMissing.of(), + @JsonProperty("sampling_params") + @ExcludeMissing + samplingParams: JsonValue = JsonMissing.of(), + @JsonProperty("pass_threshold") + @ExcludeMissing + passThreshold: JsonField = JsonMissing.of(), + ) : this(input, model, name, type, range, samplingParams, passThreshold, mutableMapOf()) + + fun toScoreModelGrader(): ScoreModelGrader = + ScoreModelGrader.builder() + .input(input) + .model(model) + .name(name) + .type(type) + .range(range) + .samplingParams(samplingParams) + .build() - /** - * 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 + /** + * The input text. This may include template strings. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun input(): List = input.getRequired("input") - companion object { + /** + * The model to use for the evaluation. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun model(): String = model.getRequired("model") + + /** + * The name of the grader. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun name(): String = name.getRequired("name") - @JvmField val USER = of("user") + /** + * The object type, which is always `score_model`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("score_model") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - @JvmField val ASSISTANT = of("assistant") + /** + * The range of the score. Defaults to `[0, 1]`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun range(): Optional> = range.getOptional("range") - @JvmField val SYSTEM = of("system") + /** The sampling parameters for the model. */ + @JsonProperty("sampling_params") + @ExcludeMissing + fun _samplingParams(): JsonValue = samplingParams - @JvmField val DEVELOPER = of("developer") + /** + * The threshold for the score. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") - @JvmStatic fun of(value: String) = Role(JsonField.of(value)) - } + /** + * Returns the raw JSON value of [input]. + * + * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("input") + @ExcludeMissing + fun _input(): JsonField> = input - /** An enum containing [Role]'s known values. */ - enum class Known { - USER, - ASSISTANT, - SYSTEM, - DEVELOPER, - } + /** + * Returns the raw JSON value of [model]. + * + * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model - /** - * An enum containing [Role]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Role] 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 { - USER, - ASSISTANT, - SYSTEM, - DEVELOPER, - /** - * An enum member indicating that [Role] was instantiated with an unknown - * value. - */ - _UNKNOWN, - } + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name - /** - * 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) { - USER -> Value.USER - ASSISTANT -> Value.ASSISTANT - SYSTEM -> Value.SYSTEM - DEVELOPER -> Value.DEVELOPER - else -> Value._UNKNOWN - } + /** + * Returns the raw JSON value of [range]. + * + * Unlike [range], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("range") @ExcludeMissing fun _range(): JsonField> = range - /** - * 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) { - USER -> Known.USER - ASSISTANT -> Known.ASSISTANT - SYSTEM -> Known.SYSTEM - DEVELOPER -> Known.DEVELOPER - else -> throw OpenAIInvalidDataException("Unknown Role: $value") - } + /** + * Returns the raw JSON value of [passThreshold]. + * + * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pass_threshold") + @ExcludeMissing + fun _passThreshold(): JsonField = passThreshold - /** - * 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") - } + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } - private var validated: Boolean = false + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - fun validate(): Role = apply { - if (validated) { - return@apply - } + fun toBuilder() = Builder().from(this) - known() - validated = true - } + companion object { - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + /** + * Returns a mutable builder for constructing an instance of [ScoreModel]. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + */ + @JvmStatic fun builder() = Builder() + } - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + /** A builder for [ScoreModel]. */ + class Builder internal constructor() { - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + private var input: JsonField>? = null + private var model: JsonField? = null + private var name: JsonField? = null + private var type: JsonValue = JsonValue.from("score_model") + private var range: JsonField>? = null + private var samplingParams: JsonValue = JsonMissing.of() + private var passThreshold: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() - return /* spotless:off */ other is Role && value == other.value /* spotless:on */ - } + @JvmSynthetic + internal fun from(scoreModel: ScoreModel) = apply { + input = scoreModel.input.map { it.toMutableList() } + model = scoreModel.model + name = scoreModel.name + type = scoreModel.type + range = scoreModel.range.map { it.toMutableList() } + samplingParams = scoreModel.samplingParams + passThreshold = scoreModel.passThreshold + additionalProperties = scoreModel.additionalProperties.toMutableMap() + } - override fun hashCode() = value.hashCode() + /** The input text. This may include template strings. */ + fun input(input: List) = input(JsonField.of(input)) - override fun toString() = value.toString() + /** + * Sets [Builder.input] to an arbitrary JSON value. + * + * You should usually call [Builder.input] with a well-typed + * `List` value instead. This method is primarily for + * setting the field to an undocumented or not yet supported value. + */ + fun input(input: JsonField>) = apply { + this.input = input.map { it.toMutableList() } } - /** The type of the message input. Always `message`. */ - class Type @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 + /** + * Adds a single [ScoreModelGrader.Input] to [Builder.input]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addInput(input: ScoreModelGrader.Input) = apply { + this.input = + (this.input ?: JsonField.of(mutableListOf())).also { + checkKnown("input", it).add(input) + } + } - companion object { + /** The model to use for the evaluation. */ + fun model(model: String) = model(JsonField.of(model)) - @JvmField val MESSAGE = of("message") + /** + * Sets [Builder.model] to an arbitrary JSON value. + * + * You should usually call [Builder.model] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun model(model: JsonField) = apply { this.model = model } - @JvmStatic fun of(value: String) = Type(JsonField.of(value)) - } + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) - /** An enum containing [Type]'s known values. */ - enum class Known { - MESSAGE - } + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun name(name: JsonField) = apply { this.name = name } - /** - * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Type] 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 { - MESSAGE, - /** - * An enum member indicating that [Type] was instantiated with an unknown - * value. - */ - _UNKNOWN, - } + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("score_model") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - /** - * 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) { - MESSAGE -> Value.MESSAGE - else -> Value._UNKNOWN - } + /** The range of the score. Defaults to `[0, 1]`. */ + fun range(range: List) = range(JsonField.of(range)) - /** - * 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) { - MESSAGE -> Known.MESSAGE - else -> throw OpenAIInvalidDataException("Unknown Type: $value") - } + /** + * Sets [Builder.range] to an arbitrary JSON value. + * + * You should usually call [Builder.range] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun range(range: JsonField>) = apply { + this.range = range.map { it.toMutableList() } + } - /** - * 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") + /** + * Adds a single [Double] to [Builder.range]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addRange(range: Double) = apply { + this.range = + (this.range ?: JsonField.of(mutableListOf())).also { + checkKnown("range", it).add(range) } + } - private var validated: Boolean = false - - fun validate(): Type = apply { - if (validated) { - return@apply - } + /** The sampling parameters for the model. */ + fun samplingParams(samplingParams: JsonValue) = apply { + this.samplingParams = samplingParams + } - known() - validated = true - } + /** The threshold for the score. */ + fun passThreshold(passThreshold: Double) = + passThreshold(JsonField.of(passThreshold)) - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + /** + * Sets [Builder.passThreshold] to an arbitrary JSON value. + * + * You should usually call [Builder.passThreshold] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun passThreshold(passThreshold: JsonField) = apply { + this.passThreshold = passThreshold + } - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - return /* spotless:off */ other is Type && value == other.value /* spotless:on */ + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) } - override fun hashCode() = value.hashCode() + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - override fun toString() = value.toString() + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + /** + * Returns an immutable instance of [ScoreModel]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ScoreModel = + ScoreModel( + checkRequired("input", input).map { it.toImmutable() }, + checkRequired("model", model), + checkRequired("name", name), + type, + (range ?: JsonMissing.of()).map { it.toImmutable() }, + samplingParams, + passThreshold, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false - return /* spotless:off */ other is Input && content == other.content && role == other.role && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ + fun validate(): ScoreModel = apply { + if (validated) { + return@apply } - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(content, role, type, additionalProperties) } - /* spotless:on */ + input().forEach { it.validate() } + model() + name() + _type().let { + if (it != JsonValue.from("score_model")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + range() + passThreshold() + validated = true + } - override fun hashCode(): Int = hashCode + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - override fun toString() = - "Input{content=$content, role=$role, type=$type, additionalProperties=$additionalProperties}" - } + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (input.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (model.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("score_model")) 1 else 0 } + + (range.asKnown().getOrNull()?.size ?: 0) + + (if (passThreshold.asKnown().isPresent) 1 else 0) override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is ScoreModel && input == other.input && model == other.model && name == other.name && type == other.type && passThreshold == other.passThreshold && range == other.range && samplingParams == other.samplingParams && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is ScoreModel && input == other.input && model == other.model && name == other.name && type == other.type && range == other.range && samplingParams == other.samplingParams && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(input, model, name, type, passThreshold, range, samplingParams, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(input, model, name, type, range, samplingParams, passThreshold, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "ScoreModel{input=$input, model=$model, name=$name, type=$type, passThreshold=$passThreshold, range=$range, samplingParams=$samplingParams, additionalProperties=$additionalProperties}" + "ScoreModel{input=$input, model=$model, name=$name, type=$type, range=$range, samplingParams=$samplingParams, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalCreateResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalCreateResponse.kt index 40073b39..f9d4d953 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalCreateResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalCreateResponse.kt @@ -15,7 +15,6 @@ 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.Enum import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing @@ -26,7 +25,11 @@ import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException -import com.openai.models.responses.ResponseInputText +import com.openai.models.graders.gradermodels.LabelModelGrader +import com.openai.models.graders.gradermodels.PythonGrader +import com.openai.models.graders.gradermodels.ScoreModelGrader +import com.openai.models.graders.gradermodels.StringCheckGrader +import com.openai.models.graders.gradermodels.TextSimilarityGrader import java.util.Collections import java.util.Objects import java.util.Optional @@ -37,7 +40,7 @@ import kotlin.jvm.optionals.getOrNull * done for your LLM integration. Like: * - Improve the quality of my chatbot * - See how well my chatbot handles customer support - * - Check if o3-mini is better at my usecase than gpt-4o + * - Check if o4-mini is better at my usecase than gpt-4o */ class EvalCreateResponse private constructor( @@ -294,10 +297,26 @@ private constructor( fun customDataSourceConfig(schema: EvalCustomDataSourceConfig.Schema) = dataSourceConfig(EvalCustomDataSourceConfig.builder().schema(schema).build()) + /** Alias for calling [dataSourceConfig] with `DataSourceConfig.ofLogs(logs)`. */ + fun dataSourceConfig(logs: DataSourceConfig.Logs) = + dataSourceConfig(DataSourceConfig.ofLogs(logs)) + + /** + * Alias for calling [dataSourceConfig] with the following: + * ```java + * DataSourceConfig.Logs.builder() + * .schema(schema) + * .build() + * ``` + */ + fun logsDataSourceConfig(schema: DataSourceConfig.Logs.Schema) = + dataSourceConfig(DataSourceConfig.Logs.builder().schema(schema).build()) + /** * Alias for calling [dataSourceConfig] with * `DataSourceConfig.ofStoredCompletions(storedCompletions)`. */ + @Deprecated("deprecated") fun dataSourceConfig(storedCompletions: EvalStoredCompletionsDataSourceConfig) = dataSourceConfig(DataSourceConfig.ofStoredCompletions(storedCompletions)) @@ -309,6 +328,7 @@ private constructor( * .build() * ``` */ + @Deprecated("deprecated") fun storedCompletionsDataSourceConfig( schema: EvalStoredCompletionsDataSourceConfig.Schema ) = dataSourceConfig(EvalStoredCompletionsDataSourceConfig.builder().schema(schema).build()) @@ -388,34 +408,43 @@ private constructor( } /** - * Alias for calling [addTestingCriterion] with `TestingCriterion.ofLabelModel(labelModel)`. + * Alias for calling [addTestingCriterion] with + * `TestingCriterion.ofLabelModelGrader(labelModelGrader)`. */ - fun addTestingCriterion(labelModel: EvalLabelModelGrader) = - addTestingCriterion(TestingCriterion.ofLabelModel(labelModel)) + fun addTestingCriterion(labelModelGrader: LabelModelGrader) = + addTestingCriterion(TestingCriterion.ofLabelModelGrader(labelModelGrader)) /** * Alias for calling [addTestingCriterion] with - * `TestingCriterion.ofStringCheck(stringCheck)`. + * `TestingCriterion.ofStringCheckGrader(stringCheckGrader)`. */ - fun addTestingCriterion(stringCheck: EvalStringCheckGrader) = - addTestingCriterion(TestingCriterion.ofStringCheck(stringCheck)) + fun addTestingCriterion(stringCheckGrader: StringCheckGrader) = + addTestingCriterion(TestingCriterion.ofStringCheckGrader(stringCheckGrader)) /** * Alias for calling [addTestingCriterion] with - * `TestingCriterion.ofTextSimilarity(textSimilarity)`. + * `TestingCriterion.ofEvalGraderTextSimilarity(evalGraderTextSimilarity)`. */ - fun addTestingCriterion(textSimilarity: EvalTextSimilarityGrader) = - addTestingCriterion(TestingCriterion.ofTextSimilarity(textSimilarity)) + fun addTestingCriterion( + evalGraderTextSimilarity: TestingCriterion.EvalGraderTextSimilarity + ) = + addTestingCriterion( + TestingCriterion.ofEvalGraderTextSimilarity(evalGraderTextSimilarity) + ) - /** Alias for calling [addTestingCriterion] with `TestingCriterion.ofPython(python)`. */ - fun addTestingCriterion(python: TestingCriterion.Python) = - addTestingCriterion(TestingCriterion.ofPython(python)) + /** + * Alias for calling [addTestingCriterion] with + * `TestingCriterion.ofEvalGraderPython(evalGraderPython)`. + */ + fun addTestingCriterion(evalGraderPython: TestingCriterion.EvalGraderPython) = + addTestingCriterion(TestingCriterion.ofEvalGraderPython(evalGraderPython)) /** - * Alias for calling [addTestingCriterion] with `TestingCriterion.ofScoreModel(scoreModel)`. + * Alias for calling [addTestingCriterion] with + * `TestingCriterion.ofEvalGraderScoreModel(evalGraderScoreModel)`. */ - fun addTestingCriterion(scoreModel: TestingCriterion.ScoreModel) = - addTestingCriterion(TestingCriterion.ofScoreModel(scoreModel)) + fun addTestingCriterion(evalGraderScoreModel: TestingCriterion.EvalGraderScoreModel) = + addTestingCriterion(TestingCriterion.ofEvalGraderScoreModel(evalGraderScoreModel)) fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -516,6 +545,7 @@ private constructor( class DataSourceConfig private constructor( private val custom: EvalCustomDataSourceConfig? = null, + private val logs: Logs? = null, private val storedCompletions: EvalStoredCompletionsDataSourceConfig? = null, private val _json: JsonValue? = null, ) { @@ -529,18 +559,23 @@ private constructor( fun custom(): Optional = Optional.ofNullable(custom) /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your stored - * completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both defined - * when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This is + * usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema returned + * by this data source config is used to defined what variables are available in your evals. + * `item` and `sample` are both defined when using this data source config. */ + fun logs(): Optional = Optional.ofNullable(logs) + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") fun storedCompletions(): Optional = Optional.ofNullable(storedCompletions) fun isCustom(): Boolean = custom != null - fun isStoredCompletions(): Boolean = storedCompletions != null + fun isLogs(): Boolean = logs != null + + @Deprecated("deprecated") fun isStoredCompletions(): Boolean = storedCompletions != null /** * A CustomDataSourceConfig which specifies the schema of your `item` and optionally @@ -551,12 +586,15 @@ private constructor( fun asCustom(): EvalCustomDataSourceConfig = custom.getOrThrow("custom") /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your stored - * completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both defined - * when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This is + * usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema returned + * by this data source config is used to defined what variables are available in your evals. + * `item` and `sample` are both defined when using this data source config. */ + fun asLogs(): Logs = logs.getOrThrow("logs") + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") fun asStoredCompletions(): EvalStoredCompletionsDataSourceConfig = storedCompletions.getOrThrow("storedCompletions") @@ -565,6 +603,7 @@ private constructor( fun accept(visitor: Visitor): T = when { custom != null -> visitor.visitCustom(custom) + logs != null -> visitor.visitLogs(logs) storedCompletions != null -> visitor.visitStoredCompletions(storedCompletions) else -> visitor.unknown(_json) } @@ -582,6 +621,10 @@ private constructor( custom.validate() } + override fun visitLogs(logs: Logs) { + logs.validate() + } + override fun visitStoredCompletions( storedCompletions: EvalStoredCompletionsDataSourceConfig ) { @@ -612,6 +655,8 @@ private constructor( object : Visitor { override fun visitCustom(custom: EvalCustomDataSourceConfig) = custom.validity() + override fun visitLogs(logs: Logs) = logs.validity() + override fun visitStoredCompletions( storedCompletions: EvalStoredCompletionsDataSourceConfig ) = storedCompletions.validity() @@ -625,14 +670,15 @@ private constructor( return true } - return /* spotless:off */ other is DataSourceConfig && custom == other.custom && storedCompletions == other.storedCompletions /* spotless:on */ + return /* spotless:off */ other is DataSourceConfig && custom == other.custom && logs == other.logs && storedCompletions == other.storedCompletions /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(custom, storedCompletions) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(custom, logs, storedCompletions) /* spotless:on */ override fun toString(): String = when { custom != null -> "DataSourceConfig{custom=$custom}" + logs != null -> "DataSourceConfig{logs=$logs}" storedCompletions != null -> "DataSourceConfig{storedCompletions=$storedCompletions}" _json != null -> "DataSourceConfig{_unknown=$_json}" @@ -651,12 +697,16 @@ private constructor( fun ofCustom(custom: EvalCustomDataSourceConfig) = DataSourceConfig(custom = custom) /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your - * stored completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both - * defined when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This + * is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema + * returned by this data source config is used to defined what variables are available + * in your evals. `item` and `sample` are both defined when using this data source + * config. */ + @JvmStatic fun ofLogs(logs: Logs) = DataSourceConfig(logs = logs) + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") @JvmStatic fun ofStoredCompletions(storedCompletions: EvalStoredCompletionsDataSourceConfig) = DataSourceConfig(storedCompletions = storedCompletions) @@ -677,12 +727,16 @@ private constructor( fun visitCustom(custom: EvalCustomDataSourceConfig): T /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your - * stored completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both - * defined when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This + * is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema + * returned by this data source config is used to defined what variables are available + * in your evals. `item` and `sample` are both defined when using this data source + * config. */ + fun visitLogs(logs: Logs): T + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") fun visitStoredCompletions(storedCompletions: EvalStoredCompletionsDataSourceConfig): T /** @@ -712,6 +766,11 @@ private constructor( ?.let { DataSourceConfig(custom = it, _json = json) } ?: DataSourceConfig(_json = json) } + "logs" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DataSourceConfig(logs = it, _json = json) + } ?: DataSourceConfig(_json = json) + } "stored_completions" -> { return tryDeserialize( node, @@ -735,6 +794,7 @@ private constructor( ) { when { value.custom != null -> generator.writeObject(value.custom) + value.logs != null -> generator.writeObject(value.logs) value.storedCompletions != null -> generator.writeObject(value.storedCompletions) value._json != null -> generator.writeObject(value._json) @@ -742,787 +802,990 @@ private constructor( } } } - } - - /** - * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing - * additional information about the object in a structured format, and querying for objects via - * API or the dashboard. - * - * Keys are strings with a maximum length of 64 characters. Values are strings with a maximum - * length of 512 characters. - */ - class Metadata - @JsonCreator - private constructor( - @com.fasterxml.jackson.annotation.JsonValue - private val additionalProperties: Map - ) { - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = additionalProperties - - fun toBuilder() = Builder().from(this) - - companion object { - - /** Returns a mutable builder for constructing an instance of [Metadata]. */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [Metadata]. */ - class Builder internal constructor() { - - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(metadata: Metadata) = apply { - additionalProperties = metadata.additionalProperties.toMutableMap() - } - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + /** + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This is + * usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema returned + * by this data source config is used to defined what variables are available in your evals. + * `item` and `sample` are both defined when using this data source config. + */ + class Logs + private constructor( + private val schema: JsonField, + private val type: JsonValue, + private val metadata: JsonField, + private val additionalProperties: MutableMap, + ) { - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + @JsonCreator + private constructor( + @JsonProperty("schema") + @ExcludeMissing + schema: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("metadata") + @ExcludeMissing + metadata: JsonField = JsonMissing.of(), + ) : this(schema, type, metadata, mutableMapOf()) - fun putAllAdditionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.putAll(additionalProperties) - } + /** + * The json schema for the run data source items. Learn how to build JSON schemas + * [here](https://json-schema.org/). + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun schema(): Schema = schema.getRequired("schema") - fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + /** + * The type of data source. Always `logs`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("logs") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** + * Set of 16 key-value pairs that can be attached to an object. This can be useful for + * storing additional information about the object in a structured format, and querying + * for objects via API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with a + * maximum length of 512 characters. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun metadata(): Optional = metadata.getOptional("metadata") /** - * Returns an immutable instance of [Metadata]. + * Returns the raw JSON value of [schema]. * - * Further updates to this [Builder] will not mutate the returned instance. + * Unlike [schema], this method doesn't throw if the JSON field has an unexpected type. */ - fun build(): Metadata = Metadata(additionalProperties.toImmutable()) - } + @JsonProperty("schema") @ExcludeMissing fun _schema(): JsonField = schema - private var validated: Boolean = false + /** + * Returns the raw JSON value of [metadata]. + * + * Unlike [metadata], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("metadata") + @ExcludeMissing + fun _metadata(): JsonField = metadata - fun validate(): Metadata = apply { - if (validated) { - return@apply + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) } - validated = true - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + fun toBuilder() = Builder().from(this) - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + companion object { - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + /** + * Returns a mutable builder for constructing an instance of [Logs]. + * + * The following fields are required: + * ```java + * .schema() + * ``` + */ + @JvmStatic fun builder() = Builder() } - return /* spotless:off */ other is Metadata && additionalProperties == other.additionalProperties /* spotless:on */ - } + /** A builder for [Logs]. */ + class Builder internal constructor() { - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(additionalProperties) } - /* spotless:on */ + private var schema: JsonField? = null + private var type: JsonValue = JsonValue.from("logs") + private var metadata: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() - override fun hashCode(): Int = hashCode + @JvmSynthetic + internal fun from(logs: Logs) = apply { + schema = logs.schema + type = logs.type + metadata = logs.metadata + additionalProperties = logs.additionalProperties.toMutableMap() + } - override fun toString() = "Metadata{additionalProperties=$additionalProperties}" - } + /** + * The json schema for the run data source items. Learn how to build JSON schemas + * [here](https://json-schema.org/). + */ + fun schema(schema: Schema) = schema(JsonField.of(schema)) - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the evaluation. - */ - @JsonDeserialize(using = TestingCriterion.Deserializer::class) - @JsonSerialize(using = TestingCriterion.Serializer::class) - class TestingCriterion - private constructor( - private val labelModel: EvalLabelModelGrader? = null, - private val stringCheck: EvalStringCheckGrader? = null, - private val textSimilarity: EvalTextSimilarityGrader? = null, - private val python: Python? = null, - private val scoreModel: ScoreModel? = null, - private val _json: JsonValue? = null, - ) { + /** + * Sets [Builder.schema] to an arbitrary JSON value. + * + * You should usually call [Builder.schema] with a well-typed [Schema] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun schema(schema: JsonField) = apply { this.schema = schema } - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - fun labelModel(): Optional = Optional.ofNullable(labelModel) + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("logs") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - /** - * A StringCheckGrader object that performs a string comparison between input and reference - * using a specified operation. - */ - fun stringCheck(): Optional = Optional.ofNullable(stringCheck) + /** + * Set of 16 key-value pairs that can be attached to an object. This can be useful + * for storing additional information about the object in a structured format, and + * querying for objects via API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with + * a maximum length of 512 characters. + */ + fun metadata(metadata: Metadata?) = metadata(JsonField.ofNullable(metadata)) - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun textSimilarity(): Optional = - Optional.ofNullable(textSimilarity) + /** Alias for calling [Builder.metadata] with `metadata.orElse(null)`. */ + fun metadata(metadata: Optional) = metadata(metadata.getOrNull()) - /** A PythonGrader object that runs a python script on the input. */ - fun python(): Optional = Optional.ofNullable(python) + /** + * Sets [Builder.metadata] to an arbitrary JSON value. + * + * You should usually call [Builder.metadata] with a well-typed [Metadata] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun metadata(metadata: JsonField) = apply { this.metadata = metadata } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun scoreModel(): Optional = Optional.ofNullable(scoreModel) + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - fun isLabelModel(): Boolean = labelModel != null + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - fun isStringCheck(): Boolean = stringCheck != null + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } - fun isTextSimilarity(): Boolean = textSimilarity != null + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - fun isPython(): Boolean = python != null + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - fun isScoreModel(): Boolean = scoreModel != null + /** + * Returns an immutable instance of [Logs]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .schema() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Logs = + Logs( + checkRequired("schema", schema), + type, + metadata, + additionalProperties.toMutableMap(), + ) + } - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - fun asLabelModel(): EvalLabelModelGrader = labelModel.getOrThrow("labelModel") + private var validated: Boolean = false - /** - * A StringCheckGrader object that performs a string comparison between input and reference - * using a specified operation. - */ - fun asStringCheck(): EvalStringCheckGrader = stringCheck.getOrThrow("stringCheck") + fun validate(): Logs = apply { + if (validated) { + return@apply + } - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun asTextSimilarity(): EvalTextSimilarityGrader = - textSimilarity.getOrThrow("textSimilarity") + schema().validate() + _type().let { + if (it != JsonValue.from("logs")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + metadata().ifPresent { it.validate() } + validated = true + } - /** A PythonGrader object that runs a python script on the input. */ - fun asPython(): Python = python.getOrThrow("python") + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun asScoreModel(): ScoreModel = scoreModel.getOrThrow("scoreModel") + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (schema.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("logs")) 1 else 0 } + + (metadata.asKnown().getOrNull()?.validity() ?: 0) - fun _json(): Optional = Optional.ofNullable(_json) + /** + * The json schema for the run data source items. Learn how to build JSON schemas + * [here](https://json-schema.org/). + */ + class Schema + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { - fun accept(visitor: Visitor): T = - when { - labelModel != null -> visitor.visitLabelModel(labelModel) - stringCheck != null -> visitor.visitStringCheck(stringCheck) - textSimilarity != null -> visitor.visitTextSimilarity(textSimilarity) - python != null -> visitor.visitPython(python) - scoreModel != null -> visitor.visitScoreModel(scoreModel) - else -> visitor.unknown(_json) - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties - private var validated: Boolean = false + fun toBuilder() = Builder().from(this) - fun validate(): TestingCriterion = apply { - if (validated) { - return@apply - } + companion object { - accept( - object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) { - labelModel.validate() + /** Returns a mutable builder for constructing an instance of [Schema]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Schema]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(schema: Schema) = apply { + additionalProperties = schema.additionalProperties.toMutableMap() } - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) { - stringCheck.validate() + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) } - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) { - textSimilarity.validate() + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) } - override fun visitPython(python: Python) { - python.validate() + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) } - override fun visitScoreModel(scoreModel: ScoreModel) { - scoreModel.validate() + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) } + + /** + * Returns an immutable instance of [Schema]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Schema = Schema(additionalProperties.toImmutable()) } - ) - validated = true - } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + private var validated: Boolean = false - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) = - labelModel.validity() + fun validate(): Schema = apply { + if (validated) { + return@apply + } - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) = - stringCheck.validity() + validated = true + } - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - textSimilarity.validity() + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - override fun visitPython(python: Python) = python.validity() + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> + !value.isNull() && !value.isMissing() + } - override fun visitScoreModel(scoreModel: ScoreModel) = scoreModel.validity() + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - override fun unknown(json: JsonValue?) = 0 + return /* spotless:off */ other is Schema && additionalProperties == other.additionalProperties /* spotless:on */ } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - return /* spotless:off */ other is TestingCriterion && labelModel == other.labelModel && stringCheck == other.stringCheck && textSimilarity == other.textSimilarity && python == other.python && scoreModel == other.scoreModel /* spotless:on */ - } + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ - override fun hashCode(): Int = /* spotless:off */ Objects.hash(labelModel, stringCheck, textSimilarity, python, scoreModel) /* spotless:on */ + override fun hashCode(): Int = hashCode - override fun toString(): String = - when { - labelModel != null -> "TestingCriterion{labelModel=$labelModel}" - stringCheck != null -> "TestingCriterion{stringCheck=$stringCheck}" - textSimilarity != null -> "TestingCriterion{textSimilarity=$textSimilarity}" - python != null -> "TestingCriterion{python=$python}" - scoreModel != null -> "TestingCriterion{scoreModel=$scoreModel}" - _json != null -> "TestingCriterion{_unknown=$_json}" - else -> throw IllegalStateException("Invalid TestingCriterion") + override fun toString() = "Schema{additionalProperties=$additionalProperties}" } - companion object { - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. + * Set of 16 key-value pairs that can be attached to an object. This can be useful for + * storing additional information about the object in a structured format, and querying + * for objects via API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with a + * maximum length of 512 characters. */ - @JvmStatic - fun ofLabelModel(labelModel: EvalLabelModelGrader) = - TestingCriterion(labelModel = labelModel) + class Metadata + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { - /** - * A StringCheckGrader object that performs a string comparison between input and - * reference using a specified operation. - */ - @JvmStatic - fun ofStringCheck(stringCheck: EvalStringCheckGrader) = - TestingCriterion(stringCheck = stringCheck) + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - @JvmStatic - fun ofTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - TestingCriterion(textSimilarity = textSimilarity) + fun toBuilder() = Builder().from(this) - /** A PythonGrader object that runs a python script on the input. */ - @JvmStatic fun ofPython(python: Python) = TestingCriterion(python = python) + companion object { - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - @JvmStatic - fun ofScoreModel(scoreModel: ScoreModel) = TestingCriterion(scoreModel = scoreModel) - } + /** Returns a mutable builder for constructing an instance of [Metadata]. */ + @JvmStatic fun builder() = Builder() + } - /** - * An interface that defines how to map each variant of [TestingCriterion] to a value of - * type [T]. - */ - interface Visitor { + /** A builder for [Metadata]. */ + class Builder internal constructor() { - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - fun visitLabelModel(labelModel: EvalLabelModelGrader): T + private var additionalProperties: MutableMap = mutableMapOf() - /** - * A StringCheckGrader object that performs a string comparison between input and - * reference using a specified operation. - */ - fun visitStringCheck(stringCheck: EvalStringCheckGrader): T + @JvmSynthetic + internal fun from(metadata: Metadata) = apply { + additionalProperties = metadata.additionalProperties.toMutableMap() + } - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader): T + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - /** A PythonGrader object that runs a python script on the input. */ - fun visitPython(python: Python): T + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun visitScoreModel(scoreModel: ScoreModel): T + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } - /** - * Maps an unknown variant of [TestingCriterion] to a value of type [T]. - * - * An instance of [TestingCriterion] 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 TestingCriterion: $json") - } - } + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - internal class Deserializer : BaseDeserializer(TestingCriterion::class) { + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - override fun ObjectCodec.deserialize(node: JsonNode): TestingCriterion { - val json = JsonValue.fromJsonNode(node) - val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() + /** + * Returns an immutable instance of [Metadata]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Metadata = Metadata(additionalProperties.toImmutable()) + } - when (type) { - "label_model" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(labelModel = it, _json = json) - } ?: TestingCriterion(_json = json) - } - "string_check" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(stringCheck = it, _json = json) - } ?: TestingCriterion(_json = json) + private var validated: Boolean = false + + fun validate(): Metadata = apply { + if (validated) { + return@apply } - "text_similarity" -> { - return tryDeserialize(node, jacksonTypeRef()) - ?.let { TestingCriterion(textSimilarity = it, _json = json) } - ?: TestingCriterion(_json = json) + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false } - "python" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(python = it, _json = json) - } ?: TestingCriterion(_json = json) + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> + !value.isNull() && !value.isMissing() } - "score_model" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(scoreModel = it, _json = json) - } ?: TestingCriterion(_json = json) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } + + return /* spotless:off */ other is Metadata && additionalProperties == other.additionalProperties /* spotless:on */ } - return TestingCriterion(_json = json) - } - } + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ - internal class Serializer : BaseSerializer(TestingCriterion::class) { + override fun hashCode(): Int = hashCode - override fun serialize( - value: TestingCriterion, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.labelModel != null -> generator.writeObject(value.labelModel) - value.stringCheck != null -> generator.writeObject(value.stringCheck) - value.textSimilarity != null -> generator.writeObject(value.textSimilarity) - value.python != null -> generator.writeObject(value.python) - value.scoreModel != null -> generator.writeObject(value.scoreModel) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid TestingCriterion") + override fun toString() = "Metadata{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } + + return /* spotless:off */ other is Logs && schema == other.schema && type == other.type && metadata == other.metadata && additionalProperties == other.additionalProperties /* spotless:on */ } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(schema, type, metadata, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Logs{schema=$schema, type=$type, metadata=$metadata, additionalProperties=$additionalProperties}" } + } - /** A PythonGrader object that runs a python script on the input. */ - class Python - private constructor( - private val name: JsonField, - private val source: JsonField, - private val type: JsonValue, - private val imageTag: JsonField, - private val passThreshold: JsonField, - private val additionalProperties: MutableMap, - ) { + /** + * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing + * additional information about the object in a structured format, and querying for objects via + * API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with a maximum + * length of 512 characters. + */ + class Metadata + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { - @JsonCreator - private constructor( - @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), - @JsonProperty("source") - @ExcludeMissing - source: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - @JsonProperty("image_tag") - @ExcludeMissing - imageTag: JsonField = JsonMissing.of(), - @JsonProperty("pass_threshold") - @ExcludeMissing - passThreshold: JsonField = JsonMissing.of(), - ) : this(name, source, type, imageTag, passThreshold, mutableMapOf()) + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties - /** - * The name of the grader. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun name(): String = name.getRequired("name") + fun toBuilder() = Builder().from(this) - /** - * The source code of the python script. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun source(): String = source.getRequired("source") + companion object { - /** - * The object type, which is always `python`. - * - * Expected to always return the following: - * ```java - * JsonValue.from("python") - * ``` - * - * However, this method can be useful for debugging and logging (e.g. if the server - * responded with an unexpected value). - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + /** Returns a mutable builder for constructing an instance of [Metadata]. */ + @JvmStatic fun builder() = Builder() + } - /** - * The image tag to use for the python script. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun imageTag(): Optional = imageTag.getOptional("image_tag") + /** A builder for [Metadata]. */ + class Builder internal constructor() { - /** - * The threshold for the score. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + private var additionalProperties: MutableMap = mutableMapOf() - /** - * Returns the raw JSON value of [name]. - * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JvmSynthetic + internal fun from(metadata: Metadata) = apply { + additionalProperties = metadata.additionalProperties.toMutableMap() + } - /** - * Returns the raw JSON value of [source]. - * - * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - /** - * Returns the raw JSON value of [imageTag]. - * - * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag + 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) + } /** - * Returns the raw JSON value of [passThreshold]. + * Returns an immutable instance of [Metadata]. * - * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected - * type. + * Further updates to this [Builder] will not mutate the returned instance. */ - @JsonProperty("pass_threshold") - @ExcludeMissing - fun _passThreshold(): JsonField = passThreshold + fun build(): Metadata = Metadata(additionalProperties.toImmutable()) + } - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) + private var validated: Boolean = false + + fun validate(): Metadata = apply { + if (validated) { + return@apply } - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) + validated = true + } - fun toBuilder() = Builder().from(this) + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - companion object { + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } - /** - * Returns a mutable builder for constructing an instance of [Python]. - * - * The following fields are required: - * ```java - * .name() - * .source() - * ``` - */ - @JvmStatic fun builder() = Builder() + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } - /** A builder for [Python]. */ - class Builder internal constructor() { + return /* spotless:off */ other is Metadata && additionalProperties == other.additionalProperties /* spotless:on */ + } - private var name: JsonField? = null - private var source: JsonField? = null - private var type: JsonValue = JsonValue.from("python") - private var imageTag: JsonField = JsonMissing.of() - private var passThreshold: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ - @JvmSynthetic - internal fun from(python: Python) = apply { - name = python.name - source = python.source - type = python.type - imageTag = python.imageTag - passThreshold = python.passThreshold - additionalProperties = python.additionalProperties.toMutableMap() - } + override fun hashCode(): Int = hashCode - /** The name of the grader. */ - fun name(name: String) = name(JsonField.of(name)) + override fun toString() = "Metadata{additionalProperties=$additionalProperties}" + } - /** - * Sets [Builder.name] to an arbitrary JSON value. - * - * You should usually call [Builder.name] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun name(name: JsonField) = apply { this.name = name } + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the evaluation. + */ + @JsonDeserialize(using = TestingCriterion.Deserializer::class) + @JsonSerialize(using = TestingCriterion.Serializer::class) + class TestingCriterion + private constructor( + private val labelModelGrader: LabelModelGrader? = null, + private val stringCheckGrader: StringCheckGrader? = null, + private val evalGraderTextSimilarity: EvalGraderTextSimilarity? = null, + private val evalGraderPython: EvalGraderPython? = null, + private val evalGraderScoreModel: EvalGraderScoreModel? = null, + private val _json: JsonValue? = null, + ) { - /** The source code of the python script. */ - fun source(source: String) = source(JsonField.of(source)) + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + fun labelModelGrader(): Optional = Optional.ofNullable(labelModelGrader) - /** - * Sets [Builder.source] to an arbitrary JSON value. - * - * You should usually call [Builder.source] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun source(source: JsonField) = apply { this.source = source } + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun stringCheckGrader(): Optional = + Optional.ofNullable(stringCheckGrader) - /** - * Sets the field to an arbitrary JSON value. - * - * It is usually unnecessary to call this method because the field defaults to the - * following: - * ```java - * JsonValue.from("python") - * ``` - * - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun type(type: JsonValue) = apply { this.type = type } + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun evalGraderTextSimilarity(): Optional = + Optional.ofNullable(evalGraderTextSimilarity) - /** The image tag to use for the python script. */ - fun imageTag(imageTag: String) = imageTag(JsonField.of(imageTag)) + /** A PythonGrader object that runs a python script on the input. */ + fun evalGraderPython(): Optional = Optional.ofNullable(evalGraderPython) - /** - * Sets [Builder.imageTag] to an arbitrary JSON value. - * - * You should usually call [Builder.imageTag] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun imageTag(imageTag: JsonField) = apply { this.imageTag = imageTag } + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun evalGraderScoreModel(): Optional = + Optional.ofNullable(evalGraderScoreModel) - /** The threshold for the score. */ - fun passThreshold(passThreshold: Double) = - passThreshold(JsonField.of(passThreshold)) + fun isLabelModelGrader(): Boolean = labelModelGrader != null - /** - * Sets [Builder.passThreshold] to an arbitrary JSON value. - * - * You should usually call [Builder.passThreshold] with a well-typed [Double] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun passThreshold(passThreshold: JsonField) = apply { - this.passThreshold = passThreshold - } + fun isStringCheckGrader(): Boolean = stringCheckGrader != null - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + fun isEvalGraderTextSimilarity(): Boolean = evalGraderTextSimilarity != null - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + fun isEvalGraderPython(): Boolean = evalGraderPython != null - fun putAllAdditionalProperties(additionalProperties: Map) = - apply { - this.additionalProperties.putAll(additionalProperties) - } + fun isEvalGraderScoreModel(): Boolean = evalGraderScoreModel != null - fun removeAdditionalProperty(key: String) = apply { - additionalProperties.remove(key) - } + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + fun asLabelModelGrader(): LabelModelGrader = labelModelGrader.getOrThrow("labelModelGrader") - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun asStringCheckGrader(): StringCheckGrader = + stringCheckGrader.getOrThrow("stringCheckGrader") - /** - * Returns an immutable instance of [Python]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .name() - * .source() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): Python = - Python( - checkRequired("name", name), - checkRequired("source", source), - type, - imageTag, - passThreshold, - additionalProperties.toMutableMap(), - ) + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun asEvalGraderTextSimilarity(): EvalGraderTextSimilarity = + evalGraderTextSimilarity.getOrThrow("evalGraderTextSimilarity") + + /** A PythonGrader object that runs a python script on the input. */ + fun asEvalGraderPython(): EvalGraderPython = evalGraderPython.getOrThrow("evalGraderPython") + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun asEvalGraderScoreModel(): EvalGraderScoreModel = + evalGraderScoreModel.getOrThrow("evalGraderScoreModel") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + labelModelGrader != null -> visitor.visitLabelModelGrader(labelModelGrader) + stringCheckGrader != null -> visitor.visitStringCheckGrader(stringCheckGrader) + evalGraderTextSimilarity != null -> + visitor.visitEvalGraderTextSimilarity(evalGraderTextSimilarity) + evalGraderPython != null -> visitor.visitEvalGraderPython(evalGraderPython) + evalGraderScoreModel != null -> + visitor.visitEvalGraderScoreModel(evalGraderScoreModel) + else -> visitor.unknown(_json) } - private var validated: Boolean = false + private var validated: Boolean = false - fun validate(): Python = apply { - if (validated) { - return@apply - } + fun validate(): TestingCriterion = apply { + if (validated) { + return@apply + } - name() - source() - _type().let { - if (it != JsonValue.from("python")) { - throw OpenAIInvalidDataException("'type' is invalid, received $it") + accept( + object : Visitor { + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) { + labelModelGrader.validate() + } + + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) { + stringCheckGrader.validate() + } + + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) { + evalGraderTextSimilarity.validate() + } + + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) { + evalGraderPython.validate() + } + + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) { + evalGraderScoreModel.validate() } } - imageTag() - passThreshold() - validated = true + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) = + labelModelGrader.validity() + + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) = + stringCheckGrader.validity() + + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) = evalGraderTextSimilarity.validity() + + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) = + evalGraderPython.validity() + + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) = evalGraderScoreModel.validity() + + override fun unknown(json: JsonValue?) = 0 } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is TestingCriterion && labelModelGrader == other.labelModelGrader && stringCheckGrader == other.stringCheckGrader && evalGraderTextSimilarity == other.evalGraderTextSimilarity && evalGraderPython == other.evalGraderPython && evalGraderScoreModel == other.evalGraderScoreModel /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(labelModelGrader, stringCheckGrader, evalGraderTextSimilarity, evalGraderPython, evalGraderScoreModel) /* spotless:on */ + + override fun toString(): String = + when { + labelModelGrader != null -> "TestingCriterion{labelModelGrader=$labelModelGrader}" + stringCheckGrader != null -> + "TestingCriterion{stringCheckGrader=$stringCheckGrader}" + evalGraderTextSimilarity != null -> + "TestingCriterion{evalGraderTextSimilarity=$evalGraderTextSimilarity}" + evalGraderPython != null -> "TestingCriterion{evalGraderPython=$evalGraderPython}" + evalGraderScoreModel != null -> + "TestingCriterion{evalGraderScoreModel=$evalGraderScoreModel}" + _json != null -> "TestingCriterion{_unknown=$_json}" + else -> throw IllegalStateException("Invalid TestingCriterion") + } + + companion object { + + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + @JvmStatic + fun ofLabelModelGrader(labelModelGrader: LabelModelGrader) = + TestingCriterion(labelModelGrader = labelModelGrader) + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + @JvmStatic + fun ofStringCheckGrader(stringCheckGrader: StringCheckGrader) = + TestingCriterion(stringCheckGrader = stringCheckGrader) + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + @JvmStatic + fun ofEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity) = + TestingCriterion(evalGraderTextSimilarity = evalGraderTextSimilarity) + + /** A PythonGrader object that runs a python script on the input. */ + @JvmStatic + fun ofEvalGraderPython(evalGraderPython: EvalGraderPython) = + TestingCriterion(evalGraderPython = evalGraderPython) + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + @JvmStatic + fun ofEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel) = + TestingCriterion(evalGraderScoreModel = evalGraderScoreModel) + } + + /** + * An interface that defines how to map each variant of [TestingCriterion] to a value of + * type [T]. + */ + interface Visitor { + + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + fun visitLabelModelGrader(labelModelGrader: LabelModelGrader): T + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader): T + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun visitEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity): T + + /** A PythonGrader object that runs a python script on the input. */ + fun visitEvalGraderPython(evalGraderPython: EvalGraderPython): T + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun visitEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel): T /** - * Returns a score indicating how many valid values are contained in this object - * recursively. + * Maps an unknown variant of [TestingCriterion] to a value of type [T]. * - * Used for best match union deserialization. + * An instance of [TestingCriterion] 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. */ - @JvmSynthetic - internal fun validity(): Int = - (if (name.asKnown().isPresent) 1 else 0) + - (if (source.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("python")) 1 else 0 } + - (if (imageTag.asKnown().isPresent) 1 else 0) + - (if (passThreshold.asKnown().isPresent) 1 else 0) + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown TestingCriterion: $json") + } + } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + internal class Deserializer : BaseDeserializer(TestingCriterion::class) { - return /* spotless:off */ other is Python && name == other.name && source == other.source && type == other.type && imageTag == other.imageTag && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ - } + override fun ObjectCodec.deserialize(node: JsonNode): TestingCriterion { + val json = JsonValue.fromJsonNode(node) - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, additionalProperties) } - /* spotless:on */ + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(labelModelGrader = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(stringCheckGrader = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(evalGraderTextSimilarity = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(evalGraderPython = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(evalGraderScoreModel = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> TestingCriterion(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } - override fun hashCode(): Int = hashCode + internal class Serializer : BaseSerializer(TestingCriterion::class) { - override fun toString() = - "Python{name=$name, source=$source, type=$type, imageTag=$imageTag, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" + override fun serialize( + value: TestingCriterion, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.labelModelGrader != null -> generator.writeObject(value.labelModelGrader) + value.stringCheckGrader != null -> + generator.writeObject(value.stringCheckGrader) + value.evalGraderTextSimilarity != null -> + generator.writeObject(value.evalGraderTextSimilarity) + value.evalGraderPython != null -> generator.writeObject(value.evalGraderPython) + value.evalGraderScoreModel != null -> + generator.writeObject(value.evalGraderScoreModel) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid TestingCriterion") + } + } } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - class ScoreModel + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + class EvalGraderTextSimilarity private constructor( - private val input: JsonField>, - private val model: JsonField, + private val evaluationMetric: JsonField, + private val input: JsonField, private val name: JsonField, + private val reference: JsonField, private val type: JsonValue, private val passThreshold: JsonField, - private val range: JsonField>, - private val samplingParams: JsonValue, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("input") + @JsonProperty("evaluation_metric") @ExcludeMissing - input: JsonField> = JsonMissing.of(), - @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + evaluationMetric: JsonField = + JsonMissing.of(), + @JsonProperty("input") @ExcludeMissing input: JsonField = JsonMissing.of(), @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("reference") + @ExcludeMissing + reference: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("pass_threshold") @ExcludeMissing passThreshold: JsonField = JsonMissing.of(), - @JsonProperty("range") - @ExcludeMissing - range: JsonField> = JsonMissing.of(), - @JsonProperty("sampling_params") - @ExcludeMissing - samplingParams: JsonValue = JsonMissing.of(), - ) : this(input, model, name, type, passThreshold, range, samplingParams, mutableMapOf()) + ) : this(evaluationMetric, input, name, reference, type, passThreshold, mutableMapOf()) + + fun toTextSimilarityGrader(): TextSimilarityGrader = + TextSimilarityGrader.builder() + .evaluationMetric(evaluationMetric) + .input(input) + .name(name) + .reference(reference) + .type(type) + .build() /** - * The input text. This may include template strings. + * The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, + * `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun input(): List = input.getRequired("input") + fun evaluationMetric(): TextSimilarityGrader.EvaluationMetric = + evaluationMetric.getRequired("evaluation_metric") /** - * The model to use for the evaluation. + * The text being graded. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun model(): String = model.getRequired("model") + fun input(): String = input.getRequired("input") /** * The name of the grader. @@ -1534,11 +1797,20 @@ private constructor( fun name(): String = name.getRequired("name") /** - * The object type, which is always `score_model`. + * The text being graded against. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun reference(): String = reference.getRequired("reference") + + /** + * The type of grader. * * Expected to always return the following: * ```java - * JsonValue.from("score_model") + * JsonValue.from("text_similarity") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1549,44 +1821,46 @@ private constructor( /** * The threshold for the score. * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). */ - fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + fun passThreshold(): Double = passThreshold.getRequired("pass_threshold") /** - * The range of the score. Defaults to `[0, 1]`. + * Returns the raw JSON value of [evaluationMetric]. * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). + * Unlike [evaluationMetric], this method doesn't throw if the JSON field has an + * unexpected type. */ - fun range(): Optional> = range.getOptional("range") - - /** The sampling parameters for the model. */ - @JsonProperty("sampling_params") + @JsonProperty("evaluation_metric") @ExcludeMissing - fun _samplingParams(): JsonValue = samplingParams + fun _evaluationMetric(): JsonField = + evaluationMetric /** * Returns the raw JSON value of [input]. * * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("input") @ExcludeMissing fun _input(): JsonField> = input + @JsonProperty("input") @ExcludeMissing fun _input(): JsonField = input /** - * Returns the raw JSON value of [model]. + * Returns the raw JSON value of [name]. * - * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name /** - * Returns the raw JSON value of [name]. + * Returns the raw JSON value of [reference]. * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected + * type. */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JsonProperty("reference") + @ExcludeMissing + fun _reference(): JsonField = reference /** * Returns the raw JSON value of [passThreshold]. @@ -1598,13 +1872,6 @@ private constructor( @ExcludeMissing fun _passThreshold(): JsonField = passThreshold - /** - * Returns the raw JSON value of [range]. - * - * Unlike [range], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("range") @ExcludeMissing fun _range(): JsonField> = range - @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { additionalProperties.put(key, value) @@ -1620,79 +1887,74 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [ScoreModel]. + * Returns a mutable builder for constructing an instance of + * [EvalGraderTextSimilarity]. * * The following fields are required: * ```java + * .evaluationMetric() * .input() - * .model() * .name() + * .reference() + * .passThreshold() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [ScoreModel]. */ + /** A builder for [EvalGraderTextSimilarity]. */ class Builder internal constructor() { - private var input: JsonField>? = null - private var model: JsonField? = null + private var evaluationMetric: JsonField? = + null + private var input: JsonField? = null private var name: JsonField? = null - private var type: JsonValue = JsonValue.from("score_model") - private var passThreshold: JsonField = JsonMissing.of() - private var range: JsonField>? = null - private var samplingParams: JsonValue = JsonMissing.of() + private var reference: JsonField? = null + private var type: JsonValue = JsonValue.from("text_similarity") + private var passThreshold: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(scoreModel: ScoreModel) = apply { - input = scoreModel.input.map { it.toMutableList() } - model = scoreModel.model - name = scoreModel.name - type = scoreModel.type - passThreshold = scoreModel.passThreshold - range = scoreModel.range.map { it.toMutableList() } - samplingParams = scoreModel.samplingParams - additionalProperties = scoreModel.additionalProperties.toMutableMap() + internal fun from(evalGraderTextSimilarity: EvalGraderTextSimilarity) = apply { + evaluationMetric = evalGraderTextSimilarity.evaluationMetric + input = evalGraderTextSimilarity.input + name = evalGraderTextSimilarity.name + reference = evalGraderTextSimilarity.reference + type = evalGraderTextSimilarity.type + passThreshold = evalGraderTextSimilarity.passThreshold + additionalProperties = + evalGraderTextSimilarity.additionalProperties.toMutableMap() } - /** The input text. This may include template strings. */ - fun input(input: List) = input(JsonField.of(input)) - /** - * Sets [Builder.input] to an arbitrary JSON value. - * - * You should usually call [Builder.input] with a well-typed `List` value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. + * The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, + * `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. */ - fun input(input: JsonField>) = apply { - this.input = input.map { it.toMutableList() } - } + fun evaluationMetric(evaluationMetric: TextSimilarityGrader.EvaluationMetric) = + evaluationMetric(JsonField.of(evaluationMetric)) /** - * Adds a single [Input] to [Builder.input]. + * Sets [Builder.evaluationMetric] to an arbitrary JSON value. * - * @throws IllegalStateException if the field was previously set to a non-list. + * You should usually call [Builder.evaluationMetric] with a well-typed + * [TextSimilarityGrader.EvaluationMetric] value instead. This method is primarily + * for setting the field to an undocumented or not yet supported value. */ - fun addInput(input: Input) = apply { - this.input = - (this.input ?: JsonField.of(mutableListOf())).also { - checkKnown("input", it).add(input) - } - } + fun evaluationMetric( + evaluationMetric: JsonField + ) = apply { this.evaluationMetric = evaluationMetric } - /** The model to use for the evaluation. */ - fun model(model: String) = model(JsonField.of(model)) + /** The text being graded. */ + fun input(input: String) = input(JsonField.of(input)) /** - * Sets [Builder.model] to an arbitrary JSON value. + * Sets [Builder.input] to an arbitrary JSON value. * - * You should usually call [Builder.model] with a well-typed [String] value instead. + * You should usually call [Builder.input] with a well-typed [String] value instead. * This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun model(model: JsonField) = apply { this.model = model } + fun input(input: JsonField) = apply { this.input = input } /** The name of the grader. */ fun name(name: String) = name(JsonField.of(name)) @@ -1706,13 +1968,25 @@ private constructor( */ fun name(name: JsonField) = apply { this.name = name } + /** The text being graded against. */ + fun reference(reference: String) = reference(JsonField.of(reference)) + + /** + * Sets [Builder.reference] to an arbitrary JSON value. + * + * You should usually call [Builder.reference] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun reference(reference: JsonField) = apply { this.reference = reference } + /** * Sets the field to an arbitrary JSON value. * * It is usually unnecessary to call this method because the field defaults to the * following: * ```java - * JsonValue.from("score_model") + * JsonValue.from("text_similarity") * ``` * * This method is primarily for setting the field to an undocumented or not yet @@ -1735,37 +2009,6 @@ private constructor( this.passThreshold = passThreshold } - /** The range of the score. Defaults to `[0, 1]`. */ - fun range(range: List) = range(JsonField.of(range)) - - /** - * Sets [Builder.range] to an arbitrary JSON value. - * - * You should usually call [Builder.range] with a well-typed `List` value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun range(range: JsonField>) = apply { - this.range = range.map { it.toMutableList() } - } - - /** - * Adds a single [Double] to [Builder.range]. - * - * @throws IllegalStateException if the field was previously set to a non-list. - */ - fun addRange(range: Double) = apply { - this.range = - (this.range ?: JsonField.of(mutableListOf())).also { - checkKnown("range", it).add(range) - } - } - - /** The sampling parameters for the model. */ - fun samplingParams(samplingParams: JsonValue) = apply { - this.samplingParams = samplingParams - } - fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() putAllAdditionalProperties(additionalProperties) @@ -1789,1069 +2032,835 @@ private constructor( } /** - * Returns an immutable instance of [ScoreModel]. + * Returns an immutable instance of [EvalGraderTextSimilarity]. * * Further updates to this [Builder] will not mutate the returned instance. * * The following fields are required: * ```java + * .evaluationMetric() * .input() - * .model() * .name() + * .reference() + * .passThreshold() * ``` * * @throws IllegalStateException if any required field is unset. */ - fun build(): ScoreModel = - ScoreModel( - checkRequired("input", input).map { it.toImmutable() }, - checkRequired("model", model), + fun build(): EvalGraderTextSimilarity = + EvalGraderTextSimilarity( + checkRequired("evaluationMetric", evaluationMetric), + checkRequired("input", input), checkRequired("name", name), + checkRequired("reference", reference), type, - passThreshold, - (range ?: JsonMissing.of()).map { it.toImmutable() }, - samplingParams, + checkRequired("passThreshold", passThreshold), additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): ScoreModel = apply { + fun validate(): EvalGraderTextSimilarity = apply { if (validated) { return@apply } - input().forEach { it.validate() } - model() + evaluationMetric().validate() + input() name() + reference() _type().let { - if (it != JsonValue.from("score_model")) { - throw OpenAIInvalidDataException("'type' is invalid, received $it") - } - } - passThreshold() - range() - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (input.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + - (if (model.asKnown().isPresent) 1 else 0) + - (if (name.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("score_model")) 1 else 0 } + - (if (passThreshold.asKnown().isPresent) 1 else 0) + - (range.asKnown().getOrNull()?.size ?: 0) - - /** - * A message input to the model with a role indicating instruction following hierarchy. - * Instructions given with the `developer` or `system` role take precedence over - * instructions given with the `user` role. Messages with the `assistant` role are - * presumed to have been generated by the model in previous interactions. - */ - class Input - private constructor( - private val content: JsonField, - private val role: JsonField, - private val type: JsonField, - private val additionalProperties: MutableMap, - ) { - - @JsonCreator - private constructor( - @JsonProperty("content") - @ExcludeMissing - content: JsonField = JsonMissing.of(), - @JsonProperty("role") @ExcludeMissing role: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), - ) : this(content, role, type, mutableMapOf()) - - /** - * Text inputs to the model - can contain template strings. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun content(): Content = content.getRequired("content") - - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun role(): Role = role.getRequired("role") - - /** - * The type of the message input. Always `message`. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun type(): Optional = type.getOptional("type") - - /** - * Returns the raw JSON value of [content]. - * - * Unlike [content], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("content") - @ExcludeMissing - fun _content(): JsonField = content - - /** - * Returns the raw JSON value of [role]. - * - * Unlike [role], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("role") @ExcludeMissing fun _role(): JsonField = role - - /** - * Returns the raw JSON value of [type]. - * - * Unlike [type], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { + if (it != JsonValue.from("text_similarity")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + passThreshold() + validated = true + } - /** - * Returns a mutable builder for constructing an instance of [Input]. - * - * The following fields are required: - * ```java - * .content() - * .role() - * ``` - */ - @JvmStatic fun builder() = Builder() + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false } - /** A builder for [Input]. */ - class Builder internal constructor() { + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (evaluationMetric.asKnown().getOrNull()?.validity() ?: 0) + + (if (input.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (reference.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("text_similarity")) 1 else 0 } + + (if (passThreshold.asKnown().isPresent) 1 else 0) - private var content: JsonField? = null - private var role: JsonField? = null - private var type: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - @JvmSynthetic - internal fun from(input: Input) = apply { - content = input.content - role = input.role - type = input.type - additionalProperties = input.additionalProperties.toMutableMap() - } + return /* spotless:off */ other is EvalGraderTextSimilarity && evaluationMetric == other.evaluationMetric && input == other.input && name == other.name && reference == other.reference && type == other.type && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ + } - /** Text inputs to the model - can contain template strings. */ - fun content(content: Content) = content(JsonField.of(content)) + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(evaluationMetric, input, name, reference, type, passThreshold, additionalProperties) } + /* spotless:on */ - /** - * Sets [Builder.content] to an arbitrary JSON value. - * - * You should usually call [Builder.content] with a well-typed [Content] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun content(content: JsonField) = apply { this.content = content } + override fun hashCode(): Int = hashCode - /** Alias for calling [content] with `Content.ofTextInput(textInput)`. */ - fun content(textInput: String) = content(Content.ofTextInput(textInput)) + override fun toString() = + "EvalGraderTextSimilarity{evaluationMetric=$evaluationMetric, input=$input, name=$name, reference=$reference, type=$type, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" + } - /** - * Alias for calling [content] with - * `Content.ofResponseInputText(responseInputText)`. - */ - fun content(responseInputText: ResponseInputText) = - content(Content.ofResponseInputText(responseInputText)) + /** A PythonGrader object that runs a python script on the input. */ + class EvalGraderPython + private constructor( + private val name: JsonField, + private val source: JsonField, + private val type: JsonValue, + private val imageTag: JsonField, + private val passThreshold: JsonField, + private val additionalProperties: MutableMap, + ) { - /** Alias for calling [content] with `Content.ofOutputText(outputText)`. */ - fun content(outputText: Content.OutputText) = - content(Content.ofOutputText(outputText)) + @JsonCreator + private constructor( + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("source") + @ExcludeMissing + source: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("image_tag") + @ExcludeMissing + imageTag: JsonField = JsonMissing.of(), + @JsonProperty("pass_threshold") + @ExcludeMissing + passThreshold: JsonField = JsonMissing.of(), + ) : this(name, source, type, imageTag, passThreshold, mutableMapOf()) - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - */ - fun role(role: Role) = role(JsonField.of(role)) + fun toPythonGrader(): PythonGrader = + PythonGrader.builder() + .name(name) + .source(source) + .type(type) + .imageTag(imageTag) + .build() - /** - * Sets [Builder.role] to an arbitrary JSON value. - * - * You should usually call [Builder.role] with a well-typed [Role] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun role(role: JsonField) = apply { this.role = role } + /** + * The name of the grader. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun name(): String = name.getRequired("name") - /** The type of the message input. Always `message`. */ - fun type(type: Type) = type(JsonField.of(type)) + /** + * The source code of the python script. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun source(): String = source.getRequired("source") - /** - * Sets [Builder.type] to an arbitrary JSON value. - * - * You should usually call [Builder.type] with a well-typed [Type] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun type(type: JsonField) = apply { this.type = type } + /** + * The object type, which is always `python`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("python") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + /** + * The image tag to use for the python script. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun imageTag(): Optional = imageTag.getOptional("image_tag") - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + /** + * The threshold for the score. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") - fun putAllAdditionalProperties(additionalProperties: Map) = - apply { - this.additionalProperties.putAll(additionalProperties) - } + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name - fun removeAdditionalProperty(key: String) = apply { - additionalProperties.remove(key) - } + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** + * Returns the raw JSON value of [imageTag]. + * + * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag - /** - * Returns an immutable instance of [Input]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .content() - * .role() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): Input = - Input( - checkRequired("content", content), - checkRequired("role", role), - type, - additionalProperties.toMutableMap(), - ) - } + /** + * Returns the raw JSON value of [passThreshold]. + * + * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pass_threshold") + @ExcludeMissing + fun _passThreshold(): JsonField = passThreshold - private var validated: Boolean = false + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } - fun validate(): Input = apply { - if (validated) { - return@apply - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - content().validate() - role().validate() - type().ifPresent { it.validate() } - validated = true - } + fun toBuilder() = Builder().from(this) - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + companion object { /** - * Returns a score indicating how many valid values are contained in this object - * recursively. + * Returns a mutable builder for constructing an instance of [EvalGraderPython]. * - * Used for best match union deserialization. + * The following fields are required: + * ```java + * .name() + * .source() + * ``` */ - @JvmSynthetic - internal fun validity(): Int = - (content.asKnown().getOrNull()?.validity() ?: 0) + - (role.asKnown().getOrNull()?.validity() ?: 0) + - (type.asKnown().getOrNull()?.validity() ?: 0) - - /** Text inputs to the model - can contain template strings. */ - @JsonDeserialize(using = Content.Deserializer::class) - @JsonSerialize(using = Content.Serializer::class) - class Content - private constructor( - private val textInput: String? = null, - private val responseInputText: ResponseInputText? = null, - private val outputText: OutputText? = null, - private val _json: JsonValue? = null, - ) { - - /** A text input to the model. */ - fun textInput(): Optional = Optional.ofNullable(textInput) - - /** A text input to the model. */ - fun responseInputText(): Optional = - Optional.ofNullable(responseInputText) - - /** A text output from the model. */ - fun outputText(): Optional = Optional.ofNullable(outputText) - - fun isTextInput(): Boolean = textInput != null - - fun isResponseInputText(): Boolean = responseInputText != null - - fun isOutputText(): Boolean = outputText != null - - /** A text input to the model. */ - fun asTextInput(): String = textInput.getOrThrow("textInput") - - /** A text input to the model. */ - fun asResponseInputText(): ResponseInputText = - responseInputText.getOrThrow("responseInputText") + @JvmStatic fun builder() = Builder() + } - /** A text output from the model. */ - fun asOutputText(): OutputText = outputText.getOrThrow("outputText") + /** A builder for [EvalGraderPython]. */ + class Builder internal constructor() { - fun _json(): Optional = Optional.ofNullable(_json) + private var name: JsonField? = null + private var source: JsonField? = null + private var type: JsonValue = JsonValue.from("python") + private var imageTag: JsonField = JsonMissing.of() + private var passThreshold: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() - fun accept(visitor: Visitor): T = - when { - textInput != null -> visitor.visitTextInput(textInput) - responseInputText != null -> - visitor.visitResponseInputText(responseInputText) - outputText != null -> visitor.visitOutputText(outputText) - else -> visitor.unknown(_json) - } + @JvmSynthetic + internal fun from(evalGraderPython: EvalGraderPython) = apply { + name = evalGraderPython.name + source = evalGraderPython.source + type = evalGraderPython.type + imageTag = evalGraderPython.imageTag + passThreshold = evalGraderPython.passThreshold + additionalProperties = evalGraderPython.additionalProperties.toMutableMap() + } - private var validated: Boolean = false + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) - fun validate(): Content = apply { - if (validated) { - return@apply - } + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun name(name: JsonField) = apply { this.name = name } - accept( - object : Visitor { - override fun visitTextInput(textInput: String) {} + /** The source code of the python script. */ + fun source(source: String) = source(JsonField.of(source)) - override fun visitResponseInputText( - responseInputText: ResponseInputText - ) { - responseInputText.validate() - } + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun source(source: JsonField) = apply { this.source = source } - override fun visitOutputText(outputText: OutputText) { - outputText.validate() - } - } - ) - validated = true - } + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("python") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + /** The image tag to use for the python script. */ + fun imageTag(imageTag: String) = imageTag(JsonField.of(imageTag)) - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitTextInput(textInput: String) = 1 + /** + * Sets [Builder.imageTag] to an arbitrary JSON value. + * + * You should usually call [Builder.imageTag] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun imageTag(imageTag: JsonField) = apply { this.imageTag = imageTag } - override fun visitResponseInputText( - responseInputText: ResponseInputText - ) = responseInputText.validity() + /** The threshold for the score. */ + fun passThreshold(passThreshold: Double) = + passThreshold(JsonField.of(passThreshold)) - override fun visitOutputText(outputText: OutputText) = - outputText.validity() + /** + * Sets [Builder.passThreshold] to an arbitrary JSON value. + * + * You should usually call [Builder.passThreshold] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun passThreshold(passThreshold: JsonField) = apply { + this.passThreshold = passThreshold + } - override fun unknown(json: JsonValue?) = 0 - } - ) + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - return /* spotless:off */ other is Content && textInput == other.textInput && responseInputText == other.responseInputText && outputText == other.outputText /* spotless:on */ + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(textInput, responseInputText, outputText) /* spotless:on */ - - override fun toString(): String = - when { - textInput != null -> "Content{textInput=$textInput}" - responseInputText != null -> - "Content{responseInputText=$responseInputText}" - outputText != null -> "Content{outputText=$outputText}" - _json != null -> "Content{_unknown=$_json}" - else -> throw IllegalStateException("Invalid Content") - } - - companion object { + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - /** A text input to the model. */ - @JvmStatic - fun ofTextInput(textInput: String) = Content(textInput = textInput) + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - /** A text input to the model. */ - @JvmStatic - fun ofResponseInputText(responseInputText: ResponseInputText) = - Content(responseInputText = responseInputText) + /** + * Returns an immutable instance of [EvalGraderPython]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .name() + * .source() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): EvalGraderPython = + EvalGraderPython( + checkRequired("name", name), + checkRequired("source", source), + type, + imageTag, + passThreshold, + additionalProperties.toMutableMap(), + ) + } - /** A text output from the model. */ - @JvmStatic - fun ofOutputText(outputText: OutputText) = Content(outputText = outputText) - } + private var validated: Boolean = false - /** - * An interface that defines how to map each variant of [Content] to a value of - * type [T]. - */ - interface Visitor { - - /** A text input to the model. */ - fun visitTextInput(textInput: String): T - - /** A text input to the model. */ - fun visitResponseInputText(responseInputText: ResponseInputText): T - - /** A text output from the model. */ - fun visitOutputText(outputText: OutputText): T - - /** - * Maps an unknown variant of [Content] to a value of type [T]. - * - * An instance of [Content] 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 Content: $json") - } - } + fun validate(): EvalGraderPython = apply { + if (validated) { + return@apply + } - internal class Deserializer : BaseDeserializer(Content::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): Content { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { Content(responseInputText = it, _json = json) }, - tryDeserialize(node, jacksonTypeRef())?.let { - Content(outputText = it, _json = json) - }, - tryDeserialize(node, jacksonTypeRef())?.let { - Content(textInput = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from array). - 0 -> Content(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } + name() + source() + _type().let { + if (it != JsonValue.from("python")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") } + } + imageTag() + passThreshold() + validated = true + } - internal class Serializer : BaseSerializer(Content::class) { - - override fun serialize( - value: Content, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.textInput != null -> generator.writeObject(value.textInput) - value.responseInputText != null -> - generator.writeObject(value.responseInputText) - value.outputText != null -> generator.writeObject(value.outputText) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid Content") - } - } - } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - /** A text output from the model. */ - class OutputText - private constructor( - private val text: JsonField, - private val type: JsonValue, - private val additionalProperties: MutableMap, - ) { + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (name.asKnown().isPresent) 1 else 0) + + (if (source.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("python")) 1 else 0 } + + (if (imageTag.asKnown().isPresent) 1 else 0) + + (if (passThreshold.asKnown().isPresent) 1 else 0) - @JsonCreator - private constructor( - @JsonProperty("text") - @ExcludeMissing - text: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - ) : this(text, type, mutableMapOf()) - - /** - * The text output from the model. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected - * type or is unexpectedly missing or null (e.g. if the server responded - * with an unexpected value). - */ - fun text(): String = text.getRequired("text") - - /** - * The type of the output text. Always `output_text`. - * - * Expected to always return the following: - * ```java - * JsonValue.from("output_text") - * ``` - * - * However, this method can be useful for debugging and logging (e.g. if the - * server responded with an unexpected value). - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - - /** - * Returns the raw JSON value of [text]. - * - * Unlike [text], this method doesn't throw if the JSON field has an - * unexpected type. - */ - @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { - - /** - * Returns a mutable builder for constructing an instance of - * [OutputText]. - * - * The following fields are required: - * ```java - * .text() - * ``` - */ - @JvmStatic fun builder() = Builder() - } + return /* spotless:off */ other is EvalGraderPython && name == other.name && source == other.source && type == other.type && imageTag == other.imageTag && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ + } - /** A builder for [OutputText]. */ - class Builder internal constructor() { - - private var text: JsonField? = null - private var type: JsonValue = JsonValue.from("output_text") - private var additionalProperties: MutableMap = - mutableMapOf() - - @JvmSynthetic - internal fun from(outputText: OutputText) = apply { - text = outputText.text - type = outputText.type - additionalProperties = - outputText.additionalProperties.toMutableMap() - } - - /** The text output from the model. */ - fun text(text: String) = text(JsonField.of(text)) - - /** - * Sets [Builder.text] to an arbitrary JSON value. - * - * You should usually call [Builder.text] with a well-typed [String] - * value instead. This method is primarily for setting the field to an - * undocumented or not yet supported value. - */ - fun text(text: JsonField) = apply { this.text = text } - - /** - * Sets the field to an arbitrary JSON value. - * - * It is usually unnecessary to call this method because the field - * defaults to the following: - * ```java - * JsonValue.from("output_text") - * ``` - * - * This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun type(type: JsonValue) = apply { this.type = type } - - 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) - } - - /** - * Returns an immutable instance of [OutputText]. - * - * Further updates to this [Builder] will not mutate the returned - * instance. - * - * The following fields are required: - * ```java - * .text() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): OutputText = - OutputText( - checkRequired("text", text), - type, - additionalProperties.toMutableMap(), - ) - } + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, additionalProperties) } + /* spotless:on */ - private var validated: Boolean = false - - fun validate(): OutputText = apply { - if (validated) { - return@apply - } - - text() - _type().let { - if (it != JsonValue.from("output_text")) { - throw OpenAIInvalidDataException( - "'type' is invalid, received $it" - ) - } - } - validated = true - } + override fun hashCode(): Int = hashCode - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this - * object recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (if (text.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("output_text")) 1 else 0 } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is OutputText && text == other.text && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ - } + override fun toString() = + "EvalGraderPython{name=$name, source=$source, type=$type, imageTag=$imageTag, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" + } - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(text, type, additionalProperties) } - /* spotless:on */ + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + class EvalGraderScoreModel + private constructor( + private val input: JsonField>, + private val model: JsonField, + private val name: JsonField, + private val type: JsonValue, + private val range: JsonField>, + private val samplingParams: JsonValue, + private val passThreshold: JsonField, + private val additionalProperties: MutableMap, + ) { - override fun hashCode(): Int = hashCode + @JsonCreator + private constructor( + @JsonProperty("input") + @ExcludeMissing + input: JsonField> = JsonMissing.of(), + @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("range") + @ExcludeMissing + range: JsonField> = JsonMissing.of(), + @JsonProperty("sampling_params") + @ExcludeMissing + samplingParams: JsonValue = JsonMissing.of(), + @JsonProperty("pass_threshold") + @ExcludeMissing + passThreshold: JsonField = JsonMissing.of(), + ) : this(input, model, name, type, range, samplingParams, passThreshold, mutableMapOf()) + + fun toScoreModelGrader(): ScoreModelGrader = + ScoreModelGrader.builder() + .input(input) + .model(model) + .name(name) + .type(type) + .range(range) + .samplingParams(samplingParams) + .build() - override fun toString() = - "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" - } - } + /** + * The input text. This may include template strings. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun input(): List = input.getRequired("input") - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - */ - class Role @JsonCreator private constructor(private val value: JsonField) : - Enum { + /** + * The model to use for the evaluation. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun model(): String = model.getRequired("model") - /** - * 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 + /** + * The name of the grader. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun name(): String = name.getRequired("name") - companion object { + /** + * The object type, which is always `score_model`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("score_model") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - @JvmField val USER = of("user") + /** + * The range of the score. Defaults to `[0, 1]`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun range(): Optional> = range.getOptional("range") - @JvmField val ASSISTANT = of("assistant") + /** The sampling parameters for the model. */ + @JsonProperty("sampling_params") + @ExcludeMissing + fun _samplingParams(): JsonValue = samplingParams - @JvmField val SYSTEM = of("system") + /** + * The threshold for the score. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") - @JvmField val DEVELOPER = of("developer") + /** + * Returns the raw JSON value of [input]. + * + * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("input") + @ExcludeMissing + fun _input(): JsonField> = input - @JvmStatic fun of(value: String) = Role(JsonField.of(value)) - } + /** + * Returns the raw JSON value of [model]. + * + * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model - /** An enum containing [Role]'s known values. */ - enum class Known { - USER, - ASSISTANT, - SYSTEM, - DEVELOPER, - } + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name - /** - * An enum containing [Role]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Role] 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 { - USER, - ASSISTANT, - SYSTEM, - DEVELOPER, - /** - * An enum member indicating that [Role] was instantiated with an unknown - * value. - */ - _UNKNOWN, - } + /** + * Returns the raw JSON value of [range]. + * + * Unlike [range], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("range") @ExcludeMissing fun _range(): JsonField> = range - /** - * 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) { - USER -> Value.USER - ASSISTANT -> Value.ASSISTANT - SYSTEM -> Value.SYSTEM - DEVELOPER -> Value.DEVELOPER - else -> Value._UNKNOWN - } + /** + * Returns the raw JSON value of [passThreshold]. + * + * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pass_threshold") + @ExcludeMissing + fun _passThreshold(): JsonField = passThreshold - /** - * 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) { - USER -> Known.USER - ASSISTANT -> Known.ASSISTANT - SYSTEM -> Known.SYSTEM - DEVELOPER -> Known.DEVELOPER - else -> throw OpenAIInvalidDataException("Unknown Role: $value") - } + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, 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") - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - private var validated: Boolean = false + fun toBuilder() = Builder().from(this) - fun validate(): Role = apply { - if (validated) { - return@apply - } + companion object { - known() - validated = true - } + /** + * Returns a mutable builder for constructing an instance of [EvalGraderScoreModel]. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + */ + @JvmStatic fun builder() = Builder() + } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + /** A builder for [EvalGraderScoreModel]. */ + class Builder internal constructor() { - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + private var input: JsonField>? = null + private var model: JsonField? = null + private var name: JsonField? = null + private var type: JsonValue = JsonValue.from("score_model") + private var range: JsonField>? = null + private var samplingParams: JsonValue = JsonMissing.of() + private var passThreshold: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + @JvmSynthetic + internal fun from(evalGraderScoreModel: EvalGraderScoreModel) = apply { + input = evalGraderScoreModel.input.map { it.toMutableList() } + model = evalGraderScoreModel.model + name = evalGraderScoreModel.name + type = evalGraderScoreModel.type + range = evalGraderScoreModel.range.map { it.toMutableList() } + samplingParams = evalGraderScoreModel.samplingParams + passThreshold = evalGraderScoreModel.passThreshold + additionalProperties = evalGraderScoreModel.additionalProperties.toMutableMap() + } - return /* spotless:off */ other is Role && value == other.value /* spotless:on */ - } + /** The input text. This may include template strings. */ + fun input(input: List) = input(JsonField.of(input)) - override fun hashCode() = value.hashCode() + /** + * Sets [Builder.input] to an arbitrary JSON value. + * + * You should usually call [Builder.input] with a well-typed + * `List` value instead. This method is primarily for + * setting the field to an undocumented or not yet supported value. + */ + fun input(input: JsonField>) = apply { + this.input = input.map { it.toMutableList() } + } - override fun toString() = value.toString() + /** + * Adds a single [ScoreModelGrader.Input] to [Builder.input]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addInput(input: ScoreModelGrader.Input) = apply { + this.input = + (this.input ?: JsonField.of(mutableListOf())).also { + checkKnown("input", it).add(input) + } } - /** The type of the message input. Always `message`. */ - class Type @JsonCreator private constructor(private val value: JsonField) : - Enum { + /** The model to use for the evaluation. */ + fun model(model: String) = model(JsonField.of(model)) - /** - * 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 + /** + * Sets [Builder.model] to an arbitrary JSON value. + * + * You should usually call [Builder.model] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun model(model: JsonField) = apply { this.model = model } - companion object { + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) - @JvmField val MESSAGE = of("message") + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun name(name: JsonField) = apply { this.name = name } - @JvmStatic fun of(value: String) = Type(JsonField.of(value)) - } + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("score_model") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - /** An enum containing [Type]'s known values. */ - enum class Known { - MESSAGE - } + /** The range of the score. Defaults to `[0, 1]`. */ + fun range(range: List) = range(JsonField.of(range)) - /** - * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Type] 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 { - MESSAGE, - /** - * An enum member indicating that [Type] was instantiated with an unknown - * value. - */ - _UNKNOWN, - } + /** + * Sets [Builder.range] to an arbitrary JSON value. + * + * You should usually call [Builder.range] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun range(range: JsonField>) = apply { + this.range = range.map { it.toMutableList() } + } - /** - * 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) { - MESSAGE -> Value.MESSAGE - else -> Value._UNKNOWN + /** + * Adds a single [Double] to [Builder.range]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addRange(range: Double) = apply { + this.range = + (this.range ?: JsonField.of(mutableListOf())).also { + checkKnown("range", it).add(range) } + } - /** - * 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) { - MESSAGE -> Known.MESSAGE - else -> throw OpenAIInvalidDataException("Unknown Type: $value") - } + /** The sampling parameters for the model. */ + fun samplingParams(samplingParams: JsonValue) = apply { + this.samplingParams = samplingParams + } - /** - * 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") - } + /** The threshold for the score. */ + fun passThreshold(passThreshold: Double) = + passThreshold(JsonField.of(passThreshold)) + + /** + * Sets [Builder.passThreshold] to an arbitrary JSON value. + * + * You should usually call [Builder.passThreshold] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun passThreshold(passThreshold: JsonField) = apply { + this.passThreshold = passThreshold + } - private var validated: Boolean = false + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - fun validate(): Type = apply { - if (validated) { - return@apply - } + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - known() - validated = true + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - return /* spotless:off */ other is Type && value == other.value /* spotless:on */ - } + /** + * Returns an immutable instance of [EvalGraderScoreModel]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): EvalGraderScoreModel = + EvalGraderScoreModel( + checkRequired("input", input).map { it.toImmutable() }, + checkRequired("model", model), + checkRequired("name", name), + type, + (range ?: JsonMissing.of()).map { it.toImmutable() }, + samplingParams, + passThreshold, + additionalProperties.toMutableMap(), + ) + } - override fun hashCode() = value.hashCode() + private var validated: Boolean = false - override fun toString() = value.toString() + fun validate(): EvalGraderScoreModel = apply { + if (validated) { + return@apply } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + input().forEach { it.validate() } + model() + name() + _type().let { + if (it != JsonValue.from("score_model")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") } - - return /* spotless:off */ other is Input && content == other.content && role == other.role && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ } + range() + passThreshold() + validated = true + } - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(content, role, type, additionalProperties) } - /* spotless:on */ - - override fun hashCode(): Int = hashCode + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - override fun toString() = - "Input{content=$content, role=$role, type=$type, additionalProperties=$additionalProperties}" - } + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (input.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (model.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("score_model")) 1 else 0 } + + (range.asKnown().getOrNull()?.size ?: 0) + + (if (passThreshold.asKnown().isPresent) 1 else 0) override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is ScoreModel && input == other.input && model == other.model && name == other.name && type == other.type && passThreshold == other.passThreshold && range == other.range && samplingParams == other.samplingParams && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is EvalGraderScoreModel && input == other.input && model == other.model && name == other.name && type == other.type && range == other.range && samplingParams == other.samplingParams && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(input, model, name, type, passThreshold, range, samplingParams, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(input, model, name, type, range, samplingParams, passThreshold, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "ScoreModel{input=$input, model=$model, name=$name, type=$type, passThreshold=$passThreshold, range=$range, samplingParams=$samplingParams, additionalProperties=$additionalProperties}" + "EvalGraderScoreModel{input=$input, model=$model, name=$name, type=$type, range=$range, samplingParams=$samplingParams, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalDeleteParams.kt index 7220bde2..74f97a4e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalDeleteParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalDeleteParams.kt @@ -4,23 +4,23 @@ package com.openai.models.evals import com.openai.core.JsonValue 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 +import kotlin.jvm.optionals.getOrNull /** Delete an evaluation. */ class EvalDeleteParams private constructor( - private val evalId: String, + private val evalId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, ) : Params { - fun evalId(): String = evalId + fun evalId(): Optional = Optional.ofNullable(evalId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -32,14 +32,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [EvalDeleteParams]. - * - * The following fields are required: - * ```java - * .evalId() - * ``` - */ + @JvmStatic fun none(): EvalDeleteParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [EvalDeleteParams]. */ @JvmStatic fun builder() = Builder() } @@ -59,7 +54,10 @@ private constructor( additionalBodyProperties = evalDeleteParams.additionalBodyProperties.toMutableMap() } - fun evalId(evalId: String) = apply { this.evalId = evalId } + fun evalId(evalId: String?) = apply { this.evalId = evalId } + + /** Alias for calling [Builder.evalId] with `evalId.orElse(null)`. */ + fun evalId(evalId: Optional) = evalId(evalId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -185,17 +183,10 @@ private constructor( * Returns an immutable instance of [EvalDeleteParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .evalId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): EvalDeleteParams = EvalDeleteParams( - checkRequired("evalId", evalId), + evalId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -207,7 +198,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> evalId + 0 -> evalId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalListPage.kt index aa65e3f9..38d95a84 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalListPage.kt @@ -2,12 +2,12 @@ package com.openai.models.evals +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.EvalService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [EvalService.list] */ @@ -16,7 +16,7 @@ private constructor( private val service: EvalService, private val params: EvalListParams, private val response: EvalListPageResponse, -) { +) : Page { /** * Delegates to [EvalListPageResponse], but gracefully handles missing data. @@ -33,19 +33,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): EvalListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + override fun nextPage(): EvalListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): EvalListParams = params @@ -114,25 +111,6 @@ private constructor( ) } - class AutoPager(private val firstPage: EvalListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalListPageAsync.kt index d23299f4..c98e16f5 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalListPageAsync.kt @@ -2,22 +2,24 @@ package com.openai.models.evals +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.EvalServiceAsync import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [EvalServiceAsync.list] */ class EvalListPageAsync private constructor( private val service: EvalServiceAsync, + private val streamHandlerExecutor: Executor, private val params: EvalListParams, private val response: EvalListPageResponse, -) { +) : PageAsync { /** * Delegates to [EvalListPageResponse], but gracefully handles missing data. @@ -34,22 +36,17 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): EvalListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = + AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): EvalListParams = params @@ -67,6 +64,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -78,18 +76,24 @@ private constructor( class Builder internal constructor() { private var service: EvalServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: EvalListParams? = null private var response: EvalListPageResponse? = null @JvmSynthetic internal fun from(evalListPageAsync: EvalListPageAsync) = apply { service = evalListPageAsync.service + streamHandlerExecutor = evalListPageAsync.streamHandlerExecutor params = evalListPageAsync.params response = evalListPageAsync.response } fun service(service: EvalServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: EvalListParams) = apply { this.params = params } @@ -104,6 +108,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -113,50 +118,22 @@ private constructor( fun build(): EvalListPageAsync = EvalListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: EvalListPageAsync) { - - fun forEach( - action: Predicate, - executor: Executor, - ): CompletableFuture { - fun CompletableFuture>.forEach( - action: (EvalListResponse) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is EvalListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is EvalListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "EvalListPageAsync{service=$service, params=$params, response=$response}" + "EvalListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalListResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalListResponse.kt index 2d3a7b7d..22930ff7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalListResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalListResponse.kt @@ -15,7 +15,6 @@ 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.Enum import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing @@ -26,7 +25,11 @@ import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException -import com.openai.models.responses.ResponseInputText +import com.openai.models.graders.gradermodels.LabelModelGrader +import com.openai.models.graders.gradermodels.PythonGrader +import com.openai.models.graders.gradermodels.ScoreModelGrader +import com.openai.models.graders.gradermodels.StringCheckGrader +import com.openai.models.graders.gradermodels.TextSimilarityGrader import java.util.Collections import java.util.Objects import java.util.Optional @@ -37,7 +40,7 @@ import kotlin.jvm.optionals.getOrNull * done for your LLM integration. Like: * - Improve the quality of my chatbot * - See how well my chatbot handles customer support - * - Check if o3-mini is better at my usecase than gpt-4o + * - Check if o4-mini is better at my usecase than gpt-4o */ class EvalListResponse private constructor( @@ -294,10 +297,26 @@ private constructor( fun customDataSourceConfig(schema: EvalCustomDataSourceConfig.Schema) = dataSourceConfig(EvalCustomDataSourceConfig.builder().schema(schema).build()) + /** Alias for calling [dataSourceConfig] with `DataSourceConfig.ofLogs(logs)`. */ + fun dataSourceConfig(logs: DataSourceConfig.Logs) = + dataSourceConfig(DataSourceConfig.ofLogs(logs)) + + /** + * Alias for calling [dataSourceConfig] with the following: + * ```java + * DataSourceConfig.Logs.builder() + * .schema(schema) + * .build() + * ``` + */ + fun logsDataSourceConfig(schema: DataSourceConfig.Logs.Schema) = + dataSourceConfig(DataSourceConfig.Logs.builder().schema(schema).build()) + /** * Alias for calling [dataSourceConfig] with * `DataSourceConfig.ofStoredCompletions(storedCompletions)`. */ + @Deprecated("deprecated") fun dataSourceConfig(storedCompletions: EvalStoredCompletionsDataSourceConfig) = dataSourceConfig(DataSourceConfig.ofStoredCompletions(storedCompletions)) @@ -309,6 +328,7 @@ private constructor( * .build() * ``` */ + @Deprecated("deprecated") fun storedCompletionsDataSourceConfig( schema: EvalStoredCompletionsDataSourceConfig.Schema ) = dataSourceConfig(EvalStoredCompletionsDataSourceConfig.builder().schema(schema).build()) @@ -388,34 +408,43 @@ private constructor( } /** - * Alias for calling [addTestingCriterion] with `TestingCriterion.ofLabelModel(labelModel)`. + * Alias for calling [addTestingCriterion] with + * `TestingCriterion.ofLabelModelGrader(labelModelGrader)`. */ - fun addTestingCriterion(labelModel: EvalLabelModelGrader) = - addTestingCriterion(TestingCriterion.ofLabelModel(labelModel)) + fun addTestingCriterion(labelModelGrader: LabelModelGrader) = + addTestingCriterion(TestingCriterion.ofLabelModelGrader(labelModelGrader)) /** * Alias for calling [addTestingCriterion] with - * `TestingCriterion.ofStringCheck(stringCheck)`. + * `TestingCriterion.ofStringCheckGrader(stringCheckGrader)`. */ - fun addTestingCriterion(stringCheck: EvalStringCheckGrader) = - addTestingCriterion(TestingCriterion.ofStringCheck(stringCheck)) + fun addTestingCriterion(stringCheckGrader: StringCheckGrader) = + addTestingCriterion(TestingCriterion.ofStringCheckGrader(stringCheckGrader)) /** * Alias for calling [addTestingCriterion] with - * `TestingCriterion.ofTextSimilarity(textSimilarity)`. + * `TestingCriterion.ofEvalGraderTextSimilarity(evalGraderTextSimilarity)`. */ - fun addTestingCriterion(textSimilarity: EvalTextSimilarityGrader) = - addTestingCriterion(TestingCriterion.ofTextSimilarity(textSimilarity)) + fun addTestingCriterion( + evalGraderTextSimilarity: TestingCriterion.EvalGraderTextSimilarity + ) = + addTestingCriterion( + TestingCriterion.ofEvalGraderTextSimilarity(evalGraderTextSimilarity) + ) - /** Alias for calling [addTestingCriterion] with `TestingCriterion.ofPython(python)`. */ - fun addTestingCriterion(python: TestingCriterion.Python) = - addTestingCriterion(TestingCriterion.ofPython(python)) + /** + * Alias for calling [addTestingCriterion] with + * `TestingCriterion.ofEvalGraderPython(evalGraderPython)`. + */ + fun addTestingCriterion(evalGraderPython: TestingCriterion.EvalGraderPython) = + addTestingCriterion(TestingCriterion.ofEvalGraderPython(evalGraderPython)) /** - * Alias for calling [addTestingCriterion] with `TestingCriterion.ofScoreModel(scoreModel)`. + * Alias for calling [addTestingCriterion] with + * `TestingCriterion.ofEvalGraderScoreModel(evalGraderScoreModel)`. */ - fun addTestingCriterion(scoreModel: TestingCriterion.ScoreModel) = - addTestingCriterion(TestingCriterion.ofScoreModel(scoreModel)) + fun addTestingCriterion(evalGraderScoreModel: TestingCriterion.EvalGraderScoreModel) = + addTestingCriterion(TestingCriterion.ofEvalGraderScoreModel(evalGraderScoreModel)) fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -516,6 +545,7 @@ private constructor( class DataSourceConfig private constructor( private val custom: EvalCustomDataSourceConfig? = null, + private val logs: Logs? = null, private val storedCompletions: EvalStoredCompletionsDataSourceConfig? = null, private val _json: JsonValue? = null, ) { @@ -529,18 +559,23 @@ private constructor( fun custom(): Optional = Optional.ofNullable(custom) /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your stored - * completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both defined - * when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This is + * usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema returned + * by this data source config is used to defined what variables are available in your evals. + * `item` and `sample` are both defined when using this data source config. */ + fun logs(): Optional = Optional.ofNullable(logs) + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") fun storedCompletions(): Optional = Optional.ofNullable(storedCompletions) fun isCustom(): Boolean = custom != null - fun isStoredCompletions(): Boolean = storedCompletions != null + fun isLogs(): Boolean = logs != null + + @Deprecated("deprecated") fun isStoredCompletions(): Boolean = storedCompletions != null /** * A CustomDataSourceConfig which specifies the schema of your `item` and optionally @@ -551,12 +586,15 @@ private constructor( fun asCustom(): EvalCustomDataSourceConfig = custom.getOrThrow("custom") /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your stored - * completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both defined - * when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This is + * usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema returned + * by this data source config is used to defined what variables are available in your evals. + * `item` and `sample` are both defined when using this data source config. */ + fun asLogs(): Logs = logs.getOrThrow("logs") + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") fun asStoredCompletions(): EvalStoredCompletionsDataSourceConfig = storedCompletions.getOrThrow("storedCompletions") @@ -565,6 +603,7 @@ private constructor( fun accept(visitor: Visitor): T = when { custom != null -> visitor.visitCustom(custom) + logs != null -> visitor.visitLogs(logs) storedCompletions != null -> visitor.visitStoredCompletions(storedCompletions) else -> visitor.unknown(_json) } @@ -582,6 +621,10 @@ private constructor( custom.validate() } + override fun visitLogs(logs: Logs) { + logs.validate() + } + override fun visitStoredCompletions( storedCompletions: EvalStoredCompletionsDataSourceConfig ) { @@ -612,6 +655,8 @@ private constructor( object : Visitor { override fun visitCustom(custom: EvalCustomDataSourceConfig) = custom.validity() + override fun visitLogs(logs: Logs) = logs.validity() + override fun visitStoredCompletions( storedCompletions: EvalStoredCompletionsDataSourceConfig ) = storedCompletions.validity() @@ -625,14 +670,15 @@ private constructor( return true } - return /* spotless:off */ other is DataSourceConfig && custom == other.custom && storedCompletions == other.storedCompletions /* spotless:on */ + return /* spotless:off */ other is DataSourceConfig && custom == other.custom && logs == other.logs && storedCompletions == other.storedCompletions /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(custom, storedCompletions) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(custom, logs, storedCompletions) /* spotless:on */ override fun toString(): String = when { custom != null -> "DataSourceConfig{custom=$custom}" + logs != null -> "DataSourceConfig{logs=$logs}" storedCompletions != null -> "DataSourceConfig{storedCompletions=$storedCompletions}" _json != null -> "DataSourceConfig{_unknown=$_json}" @@ -651,12 +697,16 @@ private constructor( fun ofCustom(custom: EvalCustomDataSourceConfig) = DataSourceConfig(custom = custom) /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your - * stored completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both - * defined when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This + * is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema + * returned by this data source config is used to defined what variables are available + * in your evals. `item` and `sample` are both defined when using this data source + * config. */ + @JvmStatic fun ofLogs(logs: Logs) = DataSourceConfig(logs = logs) + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") @JvmStatic fun ofStoredCompletions(storedCompletions: EvalStoredCompletionsDataSourceConfig) = DataSourceConfig(storedCompletions = storedCompletions) @@ -677,12 +727,16 @@ private constructor( fun visitCustom(custom: EvalCustomDataSourceConfig): T /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your - * stored completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both - * defined when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This + * is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema + * returned by this data source config is used to defined what variables are available + * in your evals. `item` and `sample` are both defined when using this data source + * config. */ + fun visitLogs(logs: Logs): T + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") fun visitStoredCompletions(storedCompletions: EvalStoredCompletionsDataSourceConfig): T /** @@ -712,6 +766,11 @@ private constructor( ?.let { DataSourceConfig(custom = it, _json = json) } ?: DataSourceConfig(_json = json) } + "logs" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DataSourceConfig(logs = it, _json = json) + } ?: DataSourceConfig(_json = json) + } "stored_completions" -> { return tryDeserialize( node, @@ -735,6 +794,7 @@ private constructor( ) { when { value.custom != null -> generator.writeObject(value.custom) + value.logs != null -> generator.writeObject(value.logs) value.storedCompletions != null -> generator.writeObject(value.storedCompletions) value._json != null -> generator.writeObject(value._json) @@ -742,787 +802,990 @@ private constructor( } } } - } - - /** - * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing - * additional information about the object in a structured format, and querying for objects via - * API or the dashboard. - * - * Keys are strings with a maximum length of 64 characters. Values are strings with a maximum - * length of 512 characters. - */ - class Metadata - @JsonCreator - private constructor( - @com.fasterxml.jackson.annotation.JsonValue - private val additionalProperties: Map - ) { - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = additionalProperties - - fun toBuilder() = Builder().from(this) - - companion object { - - /** Returns a mutable builder for constructing an instance of [Metadata]. */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [Metadata]. */ - class Builder internal constructor() { - - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(metadata: Metadata) = apply { - additionalProperties = metadata.additionalProperties.toMutableMap() - } - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + /** + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This is + * usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema returned + * by this data source config is used to defined what variables are available in your evals. + * `item` and `sample` are both defined when using this data source config. + */ + class Logs + private constructor( + private val schema: JsonField, + private val type: JsonValue, + private val metadata: JsonField, + private val additionalProperties: MutableMap, + ) { - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + @JsonCreator + private constructor( + @JsonProperty("schema") + @ExcludeMissing + schema: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("metadata") + @ExcludeMissing + metadata: JsonField = JsonMissing.of(), + ) : this(schema, type, metadata, mutableMapOf()) - fun putAllAdditionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.putAll(additionalProperties) - } + /** + * The json schema for the run data source items. Learn how to build JSON schemas + * [here](https://json-schema.org/). + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun schema(): Schema = schema.getRequired("schema") - fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + /** + * The type of data source. Always `logs`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("logs") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** + * Set of 16 key-value pairs that can be attached to an object. This can be useful for + * storing additional information about the object in a structured format, and querying + * for objects via API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with a + * maximum length of 512 characters. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun metadata(): Optional = metadata.getOptional("metadata") /** - * Returns an immutable instance of [Metadata]. + * Returns the raw JSON value of [schema]. * - * Further updates to this [Builder] will not mutate the returned instance. + * Unlike [schema], this method doesn't throw if the JSON field has an unexpected type. */ - fun build(): Metadata = Metadata(additionalProperties.toImmutable()) - } + @JsonProperty("schema") @ExcludeMissing fun _schema(): JsonField = schema - private var validated: Boolean = false + /** + * Returns the raw JSON value of [metadata]. + * + * Unlike [metadata], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("metadata") + @ExcludeMissing + fun _metadata(): JsonField = metadata - fun validate(): Metadata = apply { - if (validated) { - return@apply + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) } - validated = true - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + fun toBuilder() = Builder().from(this) - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + companion object { - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + /** + * Returns a mutable builder for constructing an instance of [Logs]. + * + * The following fields are required: + * ```java + * .schema() + * ``` + */ + @JvmStatic fun builder() = Builder() } - return /* spotless:off */ other is Metadata && additionalProperties == other.additionalProperties /* spotless:on */ - } + /** A builder for [Logs]. */ + class Builder internal constructor() { - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(additionalProperties) } - /* spotless:on */ + private var schema: JsonField? = null + private var type: JsonValue = JsonValue.from("logs") + private var metadata: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() - override fun hashCode(): Int = hashCode + @JvmSynthetic + internal fun from(logs: Logs) = apply { + schema = logs.schema + type = logs.type + metadata = logs.metadata + additionalProperties = logs.additionalProperties.toMutableMap() + } - override fun toString() = "Metadata{additionalProperties=$additionalProperties}" - } + /** + * The json schema for the run data source items. Learn how to build JSON schemas + * [here](https://json-schema.org/). + */ + fun schema(schema: Schema) = schema(JsonField.of(schema)) - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the evaluation. - */ - @JsonDeserialize(using = TestingCriterion.Deserializer::class) - @JsonSerialize(using = TestingCriterion.Serializer::class) - class TestingCriterion - private constructor( - private val labelModel: EvalLabelModelGrader? = null, - private val stringCheck: EvalStringCheckGrader? = null, - private val textSimilarity: EvalTextSimilarityGrader? = null, - private val python: Python? = null, - private val scoreModel: ScoreModel? = null, - private val _json: JsonValue? = null, - ) { + /** + * Sets [Builder.schema] to an arbitrary JSON value. + * + * You should usually call [Builder.schema] with a well-typed [Schema] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun schema(schema: JsonField) = apply { this.schema = schema } - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - fun labelModel(): Optional = Optional.ofNullable(labelModel) + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("logs") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - /** - * A StringCheckGrader object that performs a string comparison between input and reference - * using a specified operation. - */ - fun stringCheck(): Optional = Optional.ofNullable(stringCheck) + /** + * Set of 16 key-value pairs that can be attached to an object. This can be useful + * for storing additional information about the object in a structured format, and + * querying for objects via API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with + * a maximum length of 512 characters. + */ + fun metadata(metadata: Metadata?) = metadata(JsonField.ofNullable(metadata)) - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun textSimilarity(): Optional = - Optional.ofNullable(textSimilarity) + /** Alias for calling [Builder.metadata] with `metadata.orElse(null)`. */ + fun metadata(metadata: Optional) = metadata(metadata.getOrNull()) - /** A PythonGrader object that runs a python script on the input. */ - fun python(): Optional = Optional.ofNullable(python) + /** + * Sets [Builder.metadata] to an arbitrary JSON value. + * + * You should usually call [Builder.metadata] with a well-typed [Metadata] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun metadata(metadata: JsonField) = apply { this.metadata = metadata } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun scoreModel(): Optional = Optional.ofNullable(scoreModel) + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - fun isLabelModel(): Boolean = labelModel != null + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - fun isStringCheck(): Boolean = stringCheck != null + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } - fun isTextSimilarity(): Boolean = textSimilarity != null + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - fun isPython(): Boolean = python != null + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - fun isScoreModel(): Boolean = scoreModel != null + /** + * Returns an immutable instance of [Logs]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .schema() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Logs = + Logs( + checkRequired("schema", schema), + type, + metadata, + additionalProperties.toMutableMap(), + ) + } - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - fun asLabelModel(): EvalLabelModelGrader = labelModel.getOrThrow("labelModel") + private var validated: Boolean = false - /** - * A StringCheckGrader object that performs a string comparison between input and reference - * using a specified operation. - */ - fun asStringCheck(): EvalStringCheckGrader = stringCheck.getOrThrow("stringCheck") + fun validate(): Logs = apply { + if (validated) { + return@apply + } - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun asTextSimilarity(): EvalTextSimilarityGrader = - textSimilarity.getOrThrow("textSimilarity") + schema().validate() + _type().let { + if (it != JsonValue.from("logs")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + metadata().ifPresent { it.validate() } + validated = true + } - /** A PythonGrader object that runs a python script on the input. */ - fun asPython(): Python = python.getOrThrow("python") + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun asScoreModel(): ScoreModel = scoreModel.getOrThrow("scoreModel") + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (schema.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("logs")) 1 else 0 } + + (metadata.asKnown().getOrNull()?.validity() ?: 0) - fun _json(): Optional = Optional.ofNullable(_json) + /** + * The json schema for the run data source items. Learn how to build JSON schemas + * [here](https://json-schema.org/). + */ + class Schema + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { - fun accept(visitor: Visitor): T = - when { - labelModel != null -> visitor.visitLabelModel(labelModel) - stringCheck != null -> visitor.visitStringCheck(stringCheck) - textSimilarity != null -> visitor.visitTextSimilarity(textSimilarity) - python != null -> visitor.visitPython(python) - scoreModel != null -> visitor.visitScoreModel(scoreModel) - else -> visitor.unknown(_json) - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties - private var validated: Boolean = false + fun toBuilder() = Builder().from(this) - fun validate(): TestingCriterion = apply { - if (validated) { - return@apply - } + companion object { - accept( - object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) { - labelModel.validate() + /** Returns a mutable builder for constructing an instance of [Schema]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Schema]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(schema: Schema) = apply { + additionalProperties = schema.additionalProperties.toMutableMap() } - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) { - stringCheck.validate() + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) } - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) { - textSimilarity.validate() + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) } - override fun visitPython(python: Python) { - python.validate() + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) } - override fun visitScoreModel(scoreModel: ScoreModel) { - scoreModel.validate() + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) } + + /** + * Returns an immutable instance of [Schema]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Schema = Schema(additionalProperties.toImmutable()) } - ) - validated = true - } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + private var validated: Boolean = false - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) = - labelModel.validity() + fun validate(): Schema = apply { + if (validated) { + return@apply + } - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) = - stringCheck.validity() + validated = true + } - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - textSimilarity.validity() + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - override fun visitPython(python: Python) = python.validity() + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> + !value.isNull() && !value.isMissing() + } - override fun visitScoreModel(scoreModel: ScoreModel) = scoreModel.validity() + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - override fun unknown(json: JsonValue?) = 0 + return /* spotless:off */ other is Schema && additionalProperties == other.additionalProperties /* spotless:on */ } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - return /* spotless:off */ other is TestingCriterion && labelModel == other.labelModel && stringCheck == other.stringCheck && textSimilarity == other.textSimilarity && python == other.python && scoreModel == other.scoreModel /* spotless:on */ - } + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ - override fun hashCode(): Int = /* spotless:off */ Objects.hash(labelModel, stringCheck, textSimilarity, python, scoreModel) /* spotless:on */ + override fun hashCode(): Int = hashCode - override fun toString(): String = - when { - labelModel != null -> "TestingCriterion{labelModel=$labelModel}" - stringCheck != null -> "TestingCriterion{stringCheck=$stringCheck}" - textSimilarity != null -> "TestingCriterion{textSimilarity=$textSimilarity}" - python != null -> "TestingCriterion{python=$python}" - scoreModel != null -> "TestingCriterion{scoreModel=$scoreModel}" - _json != null -> "TestingCriterion{_unknown=$_json}" - else -> throw IllegalStateException("Invalid TestingCriterion") + override fun toString() = "Schema{additionalProperties=$additionalProperties}" } - companion object { - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. + * Set of 16 key-value pairs that can be attached to an object. This can be useful for + * storing additional information about the object in a structured format, and querying + * for objects via API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with a + * maximum length of 512 characters. */ - @JvmStatic - fun ofLabelModel(labelModel: EvalLabelModelGrader) = - TestingCriterion(labelModel = labelModel) + class Metadata + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { - /** - * A StringCheckGrader object that performs a string comparison between input and - * reference using a specified operation. - */ - @JvmStatic - fun ofStringCheck(stringCheck: EvalStringCheckGrader) = - TestingCriterion(stringCheck = stringCheck) + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - @JvmStatic - fun ofTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - TestingCriterion(textSimilarity = textSimilarity) + fun toBuilder() = Builder().from(this) - /** A PythonGrader object that runs a python script on the input. */ - @JvmStatic fun ofPython(python: Python) = TestingCriterion(python = python) + companion object { - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - @JvmStatic - fun ofScoreModel(scoreModel: ScoreModel) = TestingCriterion(scoreModel = scoreModel) - } + /** Returns a mutable builder for constructing an instance of [Metadata]. */ + @JvmStatic fun builder() = Builder() + } - /** - * An interface that defines how to map each variant of [TestingCriterion] to a value of - * type [T]. - */ - interface Visitor { + /** A builder for [Metadata]. */ + class Builder internal constructor() { - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - fun visitLabelModel(labelModel: EvalLabelModelGrader): T + private var additionalProperties: MutableMap = mutableMapOf() - /** - * A StringCheckGrader object that performs a string comparison between input and - * reference using a specified operation. - */ - fun visitStringCheck(stringCheck: EvalStringCheckGrader): T + @JvmSynthetic + internal fun from(metadata: Metadata) = apply { + additionalProperties = metadata.additionalProperties.toMutableMap() + } - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader): T + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - /** A PythonGrader object that runs a python script on the input. */ - fun visitPython(python: Python): T + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun visitScoreModel(scoreModel: ScoreModel): T + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } - /** - * Maps an unknown variant of [TestingCriterion] to a value of type [T]. - * - * An instance of [TestingCriterion] 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 TestingCriterion: $json") - } - } + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - internal class Deserializer : BaseDeserializer(TestingCriterion::class) { + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - override fun ObjectCodec.deserialize(node: JsonNode): TestingCriterion { - val json = JsonValue.fromJsonNode(node) - val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() + /** + * Returns an immutable instance of [Metadata]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Metadata = Metadata(additionalProperties.toImmutable()) + } - when (type) { - "label_model" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(labelModel = it, _json = json) - } ?: TestingCriterion(_json = json) - } - "string_check" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(stringCheck = it, _json = json) - } ?: TestingCriterion(_json = json) + private var validated: Boolean = false + + fun validate(): Metadata = apply { + if (validated) { + return@apply } - "text_similarity" -> { - return tryDeserialize(node, jacksonTypeRef()) - ?.let { TestingCriterion(textSimilarity = it, _json = json) } - ?: TestingCriterion(_json = json) + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false } - "python" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(python = it, _json = json) - } ?: TestingCriterion(_json = json) + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> + !value.isNull() && !value.isMissing() } - "score_model" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(scoreModel = it, _json = json) - } ?: TestingCriterion(_json = json) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } + + return /* spotless:off */ other is Metadata && additionalProperties == other.additionalProperties /* spotless:on */ } - return TestingCriterion(_json = json) - } - } + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ - internal class Serializer : BaseSerializer(TestingCriterion::class) { + override fun hashCode(): Int = hashCode - override fun serialize( - value: TestingCriterion, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.labelModel != null -> generator.writeObject(value.labelModel) - value.stringCheck != null -> generator.writeObject(value.stringCheck) - value.textSimilarity != null -> generator.writeObject(value.textSimilarity) - value.python != null -> generator.writeObject(value.python) - value.scoreModel != null -> generator.writeObject(value.scoreModel) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid TestingCriterion") + override fun toString() = "Metadata{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } + + return /* spotless:off */ other is Logs && schema == other.schema && type == other.type && metadata == other.metadata && additionalProperties == other.additionalProperties /* spotless:on */ } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(schema, type, metadata, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Logs{schema=$schema, type=$type, metadata=$metadata, additionalProperties=$additionalProperties}" } + } - /** A PythonGrader object that runs a python script on the input. */ - class Python - private constructor( - private val name: JsonField, - private val source: JsonField, - private val type: JsonValue, - private val imageTag: JsonField, - private val passThreshold: JsonField, - private val additionalProperties: MutableMap, - ) { + /** + * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing + * additional information about the object in a structured format, and querying for objects via + * API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with a maximum + * length of 512 characters. + */ + class Metadata + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { - @JsonCreator - private constructor( - @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), - @JsonProperty("source") - @ExcludeMissing - source: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - @JsonProperty("image_tag") - @ExcludeMissing - imageTag: JsonField = JsonMissing.of(), - @JsonProperty("pass_threshold") - @ExcludeMissing - passThreshold: JsonField = JsonMissing.of(), - ) : this(name, source, type, imageTag, passThreshold, mutableMapOf()) + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties - /** - * The name of the grader. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun name(): String = name.getRequired("name") + fun toBuilder() = Builder().from(this) - /** - * The source code of the python script. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun source(): String = source.getRequired("source") + companion object { - /** - * The object type, which is always `python`. - * - * Expected to always return the following: - * ```java - * JsonValue.from("python") - * ``` - * - * However, this method can be useful for debugging and logging (e.g. if the server - * responded with an unexpected value). - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + /** Returns a mutable builder for constructing an instance of [Metadata]. */ + @JvmStatic fun builder() = Builder() + } - /** - * The image tag to use for the python script. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun imageTag(): Optional = imageTag.getOptional("image_tag") + /** A builder for [Metadata]. */ + class Builder internal constructor() { - /** - * The threshold for the score. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + private var additionalProperties: MutableMap = mutableMapOf() - /** - * Returns the raw JSON value of [name]. - * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JvmSynthetic + internal fun from(metadata: Metadata) = apply { + additionalProperties = metadata.additionalProperties.toMutableMap() + } - /** - * Returns the raw JSON value of [source]. - * - * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - /** - * Returns the raw JSON value of [imageTag]. - * - * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag + 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) + } /** - * Returns the raw JSON value of [passThreshold]. + * Returns an immutable instance of [Metadata]. * - * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected - * type. + * Further updates to this [Builder] will not mutate the returned instance. */ - @JsonProperty("pass_threshold") - @ExcludeMissing - fun _passThreshold(): JsonField = passThreshold + fun build(): Metadata = Metadata(additionalProperties.toImmutable()) + } - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) + private var validated: Boolean = false + + fun validate(): Metadata = apply { + if (validated) { + return@apply } - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) + validated = true + } - fun toBuilder() = Builder().from(this) + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - companion object { + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } - /** - * Returns a mutable builder for constructing an instance of [Python]. - * - * The following fields are required: - * ```java - * .name() - * .source() - * ``` - */ - @JvmStatic fun builder() = Builder() + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } - /** A builder for [Python]. */ - class Builder internal constructor() { + return /* spotless:off */ other is Metadata && additionalProperties == other.additionalProperties /* spotless:on */ + } - private var name: JsonField? = null - private var source: JsonField? = null - private var type: JsonValue = JsonValue.from("python") - private var imageTag: JsonField = JsonMissing.of() - private var passThreshold: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ - @JvmSynthetic - internal fun from(python: Python) = apply { - name = python.name - source = python.source - type = python.type - imageTag = python.imageTag - passThreshold = python.passThreshold - additionalProperties = python.additionalProperties.toMutableMap() - } + override fun hashCode(): Int = hashCode - /** The name of the grader. */ - fun name(name: String) = name(JsonField.of(name)) + override fun toString() = "Metadata{additionalProperties=$additionalProperties}" + } - /** - * Sets [Builder.name] to an arbitrary JSON value. - * - * You should usually call [Builder.name] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun name(name: JsonField) = apply { this.name = name } + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the evaluation. + */ + @JsonDeserialize(using = TestingCriterion.Deserializer::class) + @JsonSerialize(using = TestingCriterion.Serializer::class) + class TestingCriterion + private constructor( + private val labelModelGrader: LabelModelGrader? = null, + private val stringCheckGrader: StringCheckGrader? = null, + private val evalGraderTextSimilarity: EvalGraderTextSimilarity? = null, + private val evalGraderPython: EvalGraderPython? = null, + private val evalGraderScoreModel: EvalGraderScoreModel? = null, + private val _json: JsonValue? = null, + ) { - /** The source code of the python script. */ - fun source(source: String) = source(JsonField.of(source)) + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + fun labelModelGrader(): Optional = Optional.ofNullable(labelModelGrader) - /** - * Sets [Builder.source] to an arbitrary JSON value. - * - * You should usually call [Builder.source] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun source(source: JsonField) = apply { this.source = source } + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun stringCheckGrader(): Optional = + Optional.ofNullable(stringCheckGrader) - /** - * Sets the field to an arbitrary JSON value. - * - * It is usually unnecessary to call this method because the field defaults to the - * following: - * ```java - * JsonValue.from("python") - * ``` - * - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun type(type: JsonValue) = apply { this.type = type } + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun evalGraderTextSimilarity(): Optional = + Optional.ofNullable(evalGraderTextSimilarity) - /** The image tag to use for the python script. */ - fun imageTag(imageTag: String) = imageTag(JsonField.of(imageTag)) + /** A PythonGrader object that runs a python script on the input. */ + fun evalGraderPython(): Optional = Optional.ofNullable(evalGraderPython) - /** - * Sets [Builder.imageTag] to an arbitrary JSON value. - * - * You should usually call [Builder.imageTag] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun imageTag(imageTag: JsonField) = apply { this.imageTag = imageTag } + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun evalGraderScoreModel(): Optional = + Optional.ofNullable(evalGraderScoreModel) - /** The threshold for the score. */ - fun passThreshold(passThreshold: Double) = - passThreshold(JsonField.of(passThreshold)) + fun isLabelModelGrader(): Boolean = labelModelGrader != null - /** - * Sets [Builder.passThreshold] to an arbitrary JSON value. - * - * You should usually call [Builder.passThreshold] with a well-typed [Double] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun passThreshold(passThreshold: JsonField) = apply { - this.passThreshold = passThreshold - } + fun isStringCheckGrader(): Boolean = stringCheckGrader != null - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + fun isEvalGraderTextSimilarity(): Boolean = evalGraderTextSimilarity != null - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + fun isEvalGraderPython(): Boolean = evalGraderPython != null - fun putAllAdditionalProperties(additionalProperties: Map) = - apply { - this.additionalProperties.putAll(additionalProperties) - } + fun isEvalGraderScoreModel(): Boolean = evalGraderScoreModel != null - fun removeAdditionalProperty(key: String) = apply { - additionalProperties.remove(key) - } + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + fun asLabelModelGrader(): LabelModelGrader = labelModelGrader.getOrThrow("labelModelGrader") - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun asStringCheckGrader(): StringCheckGrader = + stringCheckGrader.getOrThrow("stringCheckGrader") - /** - * Returns an immutable instance of [Python]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .name() - * .source() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): Python = - Python( - checkRequired("name", name), - checkRequired("source", source), - type, - imageTag, - passThreshold, - additionalProperties.toMutableMap(), - ) + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun asEvalGraderTextSimilarity(): EvalGraderTextSimilarity = + evalGraderTextSimilarity.getOrThrow("evalGraderTextSimilarity") + + /** A PythonGrader object that runs a python script on the input. */ + fun asEvalGraderPython(): EvalGraderPython = evalGraderPython.getOrThrow("evalGraderPython") + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun asEvalGraderScoreModel(): EvalGraderScoreModel = + evalGraderScoreModel.getOrThrow("evalGraderScoreModel") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + labelModelGrader != null -> visitor.visitLabelModelGrader(labelModelGrader) + stringCheckGrader != null -> visitor.visitStringCheckGrader(stringCheckGrader) + evalGraderTextSimilarity != null -> + visitor.visitEvalGraderTextSimilarity(evalGraderTextSimilarity) + evalGraderPython != null -> visitor.visitEvalGraderPython(evalGraderPython) + evalGraderScoreModel != null -> + visitor.visitEvalGraderScoreModel(evalGraderScoreModel) + else -> visitor.unknown(_json) } - private var validated: Boolean = false + private var validated: Boolean = false - fun validate(): Python = apply { - if (validated) { - return@apply - } + fun validate(): TestingCriterion = apply { + if (validated) { + return@apply + } - name() - source() - _type().let { - if (it != JsonValue.from("python")) { - throw OpenAIInvalidDataException("'type' is invalid, received $it") + accept( + object : Visitor { + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) { + labelModelGrader.validate() + } + + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) { + stringCheckGrader.validate() + } + + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) { + evalGraderTextSimilarity.validate() + } + + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) { + evalGraderPython.validate() + } + + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) { + evalGraderScoreModel.validate() } } - imageTag() - passThreshold() - validated = true + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) = + labelModelGrader.validity() + + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) = + stringCheckGrader.validity() + + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) = evalGraderTextSimilarity.validity() + + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) = + evalGraderPython.validity() + + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) = evalGraderScoreModel.validity() + + override fun unknown(json: JsonValue?) = 0 } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is TestingCriterion && labelModelGrader == other.labelModelGrader && stringCheckGrader == other.stringCheckGrader && evalGraderTextSimilarity == other.evalGraderTextSimilarity && evalGraderPython == other.evalGraderPython && evalGraderScoreModel == other.evalGraderScoreModel /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(labelModelGrader, stringCheckGrader, evalGraderTextSimilarity, evalGraderPython, evalGraderScoreModel) /* spotless:on */ + + override fun toString(): String = + when { + labelModelGrader != null -> "TestingCriterion{labelModelGrader=$labelModelGrader}" + stringCheckGrader != null -> + "TestingCriterion{stringCheckGrader=$stringCheckGrader}" + evalGraderTextSimilarity != null -> + "TestingCriterion{evalGraderTextSimilarity=$evalGraderTextSimilarity}" + evalGraderPython != null -> "TestingCriterion{evalGraderPython=$evalGraderPython}" + evalGraderScoreModel != null -> + "TestingCriterion{evalGraderScoreModel=$evalGraderScoreModel}" + _json != null -> "TestingCriterion{_unknown=$_json}" + else -> throw IllegalStateException("Invalid TestingCriterion") + } + + companion object { + + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + @JvmStatic + fun ofLabelModelGrader(labelModelGrader: LabelModelGrader) = + TestingCriterion(labelModelGrader = labelModelGrader) + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + @JvmStatic + fun ofStringCheckGrader(stringCheckGrader: StringCheckGrader) = + TestingCriterion(stringCheckGrader = stringCheckGrader) + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + @JvmStatic + fun ofEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity) = + TestingCriterion(evalGraderTextSimilarity = evalGraderTextSimilarity) + + /** A PythonGrader object that runs a python script on the input. */ + @JvmStatic + fun ofEvalGraderPython(evalGraderPython: EvalGraderPython) = + TestingCriterion(evalGraderPython = evalGraderPython) + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + @JvmStatic + fun ofEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel) = + TestingCriterion(evalGraderScoreModel = evalGraderScoreModel) + } + + /** + * An interface that defines how to map each variant of [TestingCriterion] to a value of + * type [T]. + */ + interface Visitor { + + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + fun visitLabelModelGrader(labelModelGrader: LabelModelGrader): T + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader): T + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun visitEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity): T + + /** A PythonGrader object that runs a python script on the input. */ + fun visitEvalGraderPython(evalGraderPython: EvalGraderPython): T + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun visitEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel): T /** - * Returns a score indicating how many valid values are contained in this object - * recursively. + * Maps an unknown variant of [TestingCriterion] to a value of type [T]. * - * Used for best match union deserialization. + * An instance of [TestingCriterion] 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. */ - @JvmSynthetic - internal fun validity(): Int = - (if (name.asKnown().isPresent) 1 else 0) + - (if (source.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("python")) 1 else 0 } + - (if (imageTag.asKnown().isPresent) 1 else 0) + - (if (passThreshold.asKnown().isPresent) 1 else 0) + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown TestingCriterion: $json") + } + } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + internal class Deserializer : BaseDeserializer(TestingCriterion::class) { - return /* spotless:off */ other is Python && name == other.name && source == other.source && type == other.type && imageTag == other.imageTag && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ - } + override fun ObjectCodec.deserialize(node: JsonNode): TestingCriterion { + val json = JsonValue.fromJsonNode(node) - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, additionalProperties) } - /* spotless:on */ + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(labelModelGrader = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(stringCheckGrader = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(evalGraderTextSimilarity = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(evalGraderPython = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(evalGraderScoreModel = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> TestingCriterion(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } - override fun hashCode(): Int = hashCode + internal class Serializer : BaseSerializer(TestingCriterion::class) { - override fun toString() = - "Python{name=$name, source=$source, type=$type, imageTag=$imageTag, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" + override fun serialize( + value: TestingCriterion, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.labelModelGrader != null -> generator.writeObject(value.labelModelGrader) + value.stringCheckGrader != null -> + generator.writeObject(value.stringCheckGrader) + value.evalGraderTextSimilarity != null -> + generator.writeObject(value.evalGraderTextSimilarity) + value.evalGraderPython != null -> generator.writeObject(value.evalGraderPython) + value.evalGraderScoreModel != null -> + generator.writeObject(value.evalGraderScoreModel) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid TestingCriterion") + } + } } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - class ScoreModel + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + class EvalGraderTextSimilarity private constructor( - private val input: JsonField>, - private val model: JsonField, + private val evaluationMetric: JsonField, + private val input: JsonField, private val name: JsonField, + private val reference: JsonField, private val type: JsonValue, private val passThreshold: JsonField, - private val range: JsonField>, - private val samplingParams: JsonValue, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("input") + @JsonProperty("evaluation_metric") @ExcludeMissing - input: JsonField> = JsonMissing.of(), - @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + evaluationMetric: JsonField = + JsonMissing.of(), + @JsonProperty("input") @ExcludeMissing input: JsonField = JsonMissing.of(), @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("reference") + @ExcludeMissing + reference: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("pass_threshold") @ExcludeMissing passThreshold: JsonField = JsonMissing.of(), - @JsonProperty("range") - @ExcludeMissing - range: JsonField> = JsonMissing.of(), - @JsonProperty("sampling_params") - @ExcludeMissing - samplingParams: JsonValue = JsonMissing.of(), - ) : this(input, model, name, type, passThreshold, range, samplingParams, mutableMapOf()) + ) : this(evaluationMetric, input, name, reference, type, passThreshold, mutableMapOf()) + + fun toTextSimilarityGrader(): TextSimilarityGrader = + TextSimilarityGrader.builder() + .evaluationMetric(evaluationMetric) + .input(input) + .name(name) + .reference(reference) + .type(type) + .build() /** - * The input text. This may include template strings. + * The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, + * `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun input(): List = input.getRequired("input") + fun evaluationMetric(): TextSimilarityGrader.EvaluationMetric = + evaluationMetric.getRequired("evaluation_metric") /** - * The model to use for the evaluation. + * The text being graded. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun model(): String = model.getRequired("model") + fun input(): String = input.getRequired("input") /** * The name of the grader. @@ -1534,11 +1797,20 @@ private constructor( fun name(): String = name.getRequired("name") /** - * The object type, which is always `score_model`. + * The text being graded against. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun reference(): String = reference.getRequired("reference") + + /** + * The type of grader. * * Expected to always return the following: * ```java - * JsonValue.from("score_model") + * JsonValue.from("text_similarity") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1549,44 +1821,46 @@ private constructor( /** * The threshold for the score. * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). */ - fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + fun passThreshold(): Double = passThreshold.getRequired("pass_threshold") /** - * The range of the score. Defaults to `[0, 1]`. + * Returns the raw JSON value of [evaluationMetric]. * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). + * Unlike [evaluationMetric], this method doesn't throw if the JSON field has an + * unexpected type. */ - fun range(): Optional> = range.getOptional("range") - - /** The sampling parameters for the model. */ - @JsonProperty("sampling_params") + @JsonProperty("evaluation_metric") @ExcludeMissing - fun _samplingParams(): JsonValue = samplingParams + fun _evaluationMetric(): JsonField = + evaluationMetric /** * Returns the raw JSON value of [input]. * * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("input") @ExcludeMissing fun _input(): JsonField> = input + @JsonProperty("input") @ExcludeMissing fun _input(): JsonField = input /** - * Returns the raw JSON value of [model]. + * Returns the raw JSON value of [name]. * - * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name /** - * Returns the raw JSON value of [name]. + * Returns the raw JSON value of [reference]. * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected + * type. */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JsonProperty("reference") + @ExcludeMissing + fun _reference(): JsonField = reference /** * Returns the raw JSON value of [passThreshold]. @@ -1598,13 +1872,6 @@ private constructor( @ExcludeMissing fun _passThreshold(): JsonField = passThreshold - /** - * Returns the raw JSON value of [range]. - * - * Unlike [range], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("range") @ExcludeMissing fun _range(): JsonField> = range - @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { additionalProperties.put(key, value) @@ -1620,79 +1887,74 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [ScoreModel]. + * Returns a mutable builder for constructing an instance of + * [EvalGraderTextSimilarity]. * * The following fields are required: * ```java + * .evaluationMetric() * .input() - * .model() * .name() + * .reference() + * .passThreshold() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [ScoreModel]. */ + /** A builder for [EvalGraderTextSimilarity]. */ class Builder internal constructor() { - private var input: JsonField>? = null - private var model: JsonField? = null + private var evaluationMetric: JsonField? = + null + private var input: JsonField? = null private var name: JsonField? = null - private var type: JsonValue = JsonValue.from("score_model") - private var passThreshold: JsonField = JsonMissing.of() - private var range: JsonField>? = null - private var samplingParams: JsonValue = JsonMissing.of() + private var reference: JsonField? = null + private var type: JsonValue = JsonValue.from("text_similarity") + private var passThreshold: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(scoreModel: ScoreModel) = apply { - input = scoreModel.input.map { it.toMutableList() } - model = scoreModel.model - name = scoreModel.name - type = scoreModel.type - passThreshold = scoreModel.passThreshold - range = scoreModel.range.map { it.toMutableList() } - samplingParams = scoreModel.samplingParams - additionalProperties = scoreModel.additionalProperties.toMutableMap() + internal fun from(evalGraderTextSimilarity: EvalGraderTextSimilarity) = apply { + evaluationMetric = evalGraderTextSimilarity.evaluationMetric + input = evalGraderTextSimilarity.input + name = evalGraderTextSimilarity.name + reference = evalGraderTextSimilarity.reference + type = evalGraderTextSimilarity.type + passThreshold = evalGraderTextSimilarity.passThreshold + additionalProperties = + evalGraderTextSimilarity.additionalProperties.toMutableMap() } - /** The input text. This may include template strings. */ - fun input(input: List) = input(JsonField.of(input)) - /** - * Sets [Builder.input] to an arbitrary JSON value. - * - * You should usually call [Builder.input] with a well-typed `List` value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. + * The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, + * `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. */ - fun input(input: JsonField>) = apply { - this.input = input.map { it.toMutableList() } - } + fun evaluationMetric(evaluationMetric: TextSimilarityGrader.EvaluationMetric) = + evaluationMetric(JsonField.of(evaluationMetric)) /** - * Adds a single [Input] to [Builder.input]. + * Sets [Builder.evaluationMetric] to an arbitrary JSON value. * - * @throws IllegalStateException if the field was previously set to a non-list. + * You should usually call [Builder.evaluationMetric] with a well-typed + * [TextSimilarityGrader.EvaluationMetric] value instead. This method is primarily + * for setting the field to an undocumented or not yet supported value. */ - fun addInput(input: Input) = apply { - this.input = - (this.input ?: JsonField.of(mutableListOf())).also { - checkKnown("input", it).add(input) - } - } + fun evaluationMetric( + evaluationMetric: JsonField + ) = apply { this.evaluationMetric = evaluationMetric } - /** The model to use for the evaluation. */ - fun model(model: String) = model(JsonField.of(model)) + /** The text being graded. */ + fun input(input: String) = input(JsonField.of(input)) /** - * Sets [Builder.model] to an arbitrary JSON value. + * Sets [Builder.input] to an arbitrary JSON value. * - * You should usually call [Builder.model] with a well-typed [String] value instead. + * You should usually call [Builder.input] with a well-typed [String] value instead. * This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun model(model: JsonField) = apply { this.model = model } + fun input(input: JsonField) = apply { this.input = input } /** The name of the grader. */ fun name(name: String) = name(JsonField.of(name)) @@ -1706,13 +1968,25 @@ private constructor( */ fun name(name: JsonField) = apply { this.name = name } + /** The text being graded against. */ + fun reference(reference: String) = reference(JsonField.of(reference)) + + /** + * Sets [Builder.reference] to an arbitrary JSON value. + * + * You should usually call [Builder.reference] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun reference(reference: JsonField) = apply { this.reference = reference } + /** * Sets the field to an arbitrary JSON value. * * It is usually unnecessary to call this method because the field defaults to the * following: * ```java - * JsonValue.from("score_model") + * JsonValue.from("text_similarity") * ``` * * This method is primarily for setting the field to an undocumented or not yet @@ -1735,37 +2009,6 @@ private constructor( this.passThreshold = passThreshold } - /** The range of the score. Defaults to `[0, 1]`. */ - fun range(range: List) = range(JsonField.of(range)) - - /** - * Sets [Builder.range] to an arbitrary JSON value. - * - * You should usually call [Builder.range] with a well-typed `List` value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun range(range: JsonField>) = apply { - this.range = range.map { it.toMutableList() } - } - - /** - * Adds a single [Double] to [Builder.range]. - * - * @throws IllegalStateException if the field was previously set to a non-list. - */ - fun addRange(range: Double) = apply { - this.range = - (this.range ?: JsonField.of(mutableListOf())).also { - checkKnown("range", it).add(range) - } - } - - /** The sampling parameters for the model. */ - fun samplingParams(samplingParams: JsonValue) = apply { - this.samplingParams = samplingParams - } - fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() putAllAdditionalProperties(additionalProperties) @@ -1789,1069 +2032,835 @@ private constructor( } /** - * Returns an immutable instance of [ScoreModel]. + * Returns an immutable instance of [EvalGraderTextSimilarity]. * * Further updates to this [Builder] will not mutate the returned instance. * * The following fields are required: * ```java + * .evaluationMetric() * .input() - * .model() * .name() + * .reference() + * .passThreshold() * ``` * * @throws IllegalStateException if any required field is unset. */ - fun build(): ScoreModel = - ScoreModel( - checkRequired("input", input).map { it.toImmutable() }, - checkRequired("model", model), + fun build(): EvalGraderTextSimilarity = + EvalGraderTextSimilarity( + checkRequired("evaluationMetric", evaluationMetric), + checkRequired("input", input), checkRequired("name", name), + checkRequired("reference", reference), type, - passThreshold, - (range ?: JsonMissing.of()).map { it.toImmutable() }, - samplingParams, + checkRequired("passThreshold", passThreshold), additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): ScoreModel = apply { + fun validate(): EvalGraderTextSimilarity = apply { if (validated) { return@apply } - input().forEach { it.validate() } - model() + evaluationMetric().validate() + input() name() + reference() _type().let { - if (it != JsonValue.from("score_model")) { - throw OpenAIInvalidDataException("'type' is invalid, received $it") - } - } - passThreshold() - range() - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (input.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + - (if (model.asKnown().isPresent) 1 else 0) + - (if (name.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("score_model")) 1 else 0 } + - (if (passThreshold.asKnown().isPresent) 1 else 0) + - (range.asKnown().getOrNull()?.size ?: 0) - - /** - * A message input to the model with a role indicating instruction following hierarchy. - * Instructions given with the `developer` or `system` role take precedence over - * instructions given with the `user` role. Messages with the `assistant` role are - * presumed to have been generated by the model in previous interactions. - */ - class Input - private constructor( - private val content: JsonField, - private val role: JsonField, - private val type: JsonField, - private val additionalProperties: MutableMap, - ) { - - @JsonCreator - private constructor( - @JsonProperty("content") - @ExcludeMissing - content: JsonField = JsonMissing.of(), - @JsonProperty("role") @ExcludeMissing role: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), - ) : this(content, role, type, mutableMapOf()) - - /** - * Text inputs to the model - can contain template strings. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun content(): Content = content.getRequired("content") - - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun role(): Role = role.getRequired("role") - - /** - * The type of the message input. Always `message`. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun type(): Optional = type.getOptional("type") - - /** - * Returns the raw JSON value of [content]. - * - * Unlike [content], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("content") - @ExcludeMissing - fun _content(): JsonField = content - - /** - * Returns the raw JSON value of [role]. - * - * Unlike [role], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("role") @ExcludeMissing fun _role(): JsonField = role - - /** - * Returns the raw JSON value of [type]. - * - * Unlike [type], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { + if (it != JsonValue.from("text_similarity")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + passThreshold() + validated = true + } - /** - * Returns a mutable builder for constructing an instance of [Input]. - * - * The following fields are required: - * ```java - * .content() - * .role() - * ``` - */ - @JvmStatic fun builder() = Builder() + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false } - /** A builder for [Input]. */ - class Builder internal constructor() { + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (evaluationMetric.asKnown().getOrNull()?.validity() ?: 0) + + (if (input.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (reference.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("text_similarity")) 1 else 0 } + + (if (passThreshold.asKnown().isPresent) 1 else 0) - private var content: JsonField? = null - private var role: JsonField? = null - private var type: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - @JvmSynthetic - internal fun from(input: Input) = apply { - content = input.content - role = input.role - type = input.type - additionalProperties = input.additionalProperties.toMutableMap() - } + return /* spotless:off */ other is EvalGraderTextSimilarity && evaluationMetric == other.evaluationMetric && input == other.input && name == other.name && reference == other.reference && type == other.type && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ + } - /** Text inputs to the model - can contain template strings. */ - fun content(content: Content) = content(JsonField.of(content)) + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(evaluationMetric, input, name, reference, type, passThreshold, additionalProperties) } + /* spotless:on */ - /** - * Sets [Builder.content] to an arbitrary JSON value. - * - * You should usually call [Builder.content] with a well-typed [Content] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun content(content: JsonField) = apply { this.content = content } + override fun hashCode(): Int = hashCode - /** Alias for calling [content] with `Content.ofTextInput(textInput)`. */ - fun content(textInput: String) = content(Content.ofTextInput(textInput)) + override fun toString() = + "EvalGraderTextSimilarity{evaluationMetric=$evaluationMetric, input=$input, name=$name, reference=$reference, type=$type, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" + } - /** - * Alias for calling [content] with - * `Content.ofResponseInputText(responseInputText)`. - */ - fun content(responseInputText: ResponseInputText) = - content(Content.ofResponseInputText(responseInputText)) + /** A PythonGrader object that runs a python script on the input. */ + class EvalGraderPython + private constructor( + private val name: JsonField, + private val source: JsonField, + private val type: JsonValue, + private val imageTag: JsonField, + private val passThreshold: JsonField, + private val additionalProperties: MutableMap, + ) { - /** Alias for calling [content] with `Content.ofOutputText(outputText)`. */ - fun content(outputText: Content.OutputText) = - content(Content.ofOutputText(outputText)) + @JsonCreator + private constructor( + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("source") + @ExcludeMissing + source: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("image_tag") + @ExcludeMissing + imageTag: JsonField = JsonMissing.of(), + @JsonProperty("pass_threshold") + @ExcludeMissing + passThreshold: JsonField = JsonMissing.of(), + ) : this(name, source, type, imageTag, passThreshold, mutableMapOf()) - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - */ - fun role(role: Role) = role(JsonField.of(role)) + fun toPythonGrader(): PythonGrader = + PythonGrader.builder() + .name(name) + .source(source) + .type(type) + .imageTag(imageTag) + .build() - /** - * Sets [Builder.role] to an arbitrary JSON value. - * - * You should usually call [Builder.role] with a well-typed [Role] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun role(role: JsonField) = apply { this.role = role } + /** + * The name of the grader. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun name(): String = name.getRequired("name") - /** The type of the message input. Always `message`. */ - fun type(type: Type) = type(JsonField.of(type)) + /** + * The source code of the python script. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun source(): String = source.getRequired("source") - /** - * Sets [Builder.type] to an arbitrary JSON value. - * - * You should usually call [Builder.type] with a well-typed [Type] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun type(type: JsonField) = apply { this.type = type } + /** + * The object type, which is always `python`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("python") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + /** + * The image tag to use for the python script. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun imageTag(): Optional = imageTag.getOptional("image_tag") - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + /** + * The threshold for the score. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") - fun putAllAdditionalProperties(additionalProperties: Map) = - apply { - this.additionalProperties.putAll(additionalProperties) - } + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name - fun removeAdditionalProperty(key: String) = apply { - additionalProperties.remove(key) - } + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** + * Returns the raw JSON value of [imageTag]. + * + * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag - /** - * Returns an immutable instance of [Input]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .content() - * .role() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): Input = - Input( - checkRequired("content", content), - checkRequired("role", role), - type, - additionalProperties.toMutableMap(), - ) - } + /** + * Returns the raw JSON value of [passThreshold]. + * + * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pass_threshold") + @ExcludeMissing + fun _passThreshold(): JsonField = passThreshold - private var validated: Boolean = false + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } - fun validate(): Input = apply { - if (validated) { - return@apply - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - content().validate() - role().validate() - type().ifPresent { it.validate() } - validated = true - } + fun toBuilder() = Builder().from(this) - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + companion object { /** - * Returns a score indicating how many valid values are contained in this object - * recursively. + * Returns a mutable builder for constructing an instance of [EvalGraderPython]. * - * Used for best match union deserialization. + * The following fields are required: + * ```java + * .name() + * .source() + * ``` */ - @JvmSynthetic - internal fun validity(): Int = - (content.asKnown().getOrNull()?.validity() ?: 0) + - (role.asKnown().getOrNull()?.validity() ?: 0) + - (type.asKnown().getOrNull()?.validity() ?: 0) - - /** Text inputs to the model - can contain template strings. */ - @JsonDeserialize(using = Content.Deserializer::class) - @JsonSerialize(using = Content.Serializer::class) - class Content - private constructor( - private val textInput: String? = null, - private val responseInputText: ResponseInputText? = null, - private val outputText: OutputText? = null, - private val _json: JsonValue? = null, - ) { - - /** A text input to the model. */ - fun textInput(): Optional = Optional.ofNullable(textInput) - - /** A text input to the model. */ - fun responseInputText(): Optional = - Optional.ofNullable(responseInputText) - - /** A text output from the model. */ - fun outputText(): Optional = Optional.ofNullable(outputText) - - fun isTextInput(): Boolean = textInput != null - - fun isResponseInputText(): Boolean = responseInputText != null - - fun isOutputText(): Boolean = outputText != null - - /** A text input to the model. */ - fun asTextInput(): String = textInput.getOrThrow("textInput") - - /** A text input to the model. */ - fun asResponseInputText(): ResponseInputText = - responseInputText.getOrThrow("responseInputText") + @JvmStatic fun builder() = Builder() + } - /** A text output from the model. */ - fun asOutputText(): OutputText = outputText.getOrThrow("outputText") + /** A builder for [EvalGraderPython]. */ + class Builder internal constructor() { - fun _json(): Optional = Optional.ofNullable(_json) + private var name: JsonField? = null + private var source: JsonField? = null + private var type: JsonValue = JsonValue.from("python") + private var imageTag: JsonField = JsonMissing.of() + private var passThreshold: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() - fun accept(visitor: Visitor): T = - when { - textInput != null -> visitor.visitTextInput(textInput) - responseInputText != null -> - visitor.visitResponseInputText(responseInputText) - outputText != null -> visitor.visitOutputText(outputText) - else -> visitor.unknown(_json) - } + @JvmSynthetic + internal fun from(evalGraderPython: EvalGraderPython) = apply { + name = evalGraderPython.name + source = evalGraderPython.source + type = evalGraderPython.type + imageTag = evalGraderPython.imageTag + passThreshold = evalGraderPython.passThreshold + additionalProperties = evalGraderPython.additionalProperties.toMutableMap() + } - private var validated: Boolean = false + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) - fun validate(): Content = apply { - if (validated) { - return@apply - } + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun name(name: JsonField) = apply { this.name = name } - accept( - object : Visitor { - override fun visitTextInput(textInput: String) {} + /** The source code of the python script. */ + fun source(source: String) = source(JsonField.of(source)) - override fun visitResponseInputText( - responseInputText: ResponseInputText - ) { - responseInputText.validate() - } + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun source(source: JsonField) = apply { this.source = source } - override fun visitOutputText(outputText: OutputText) { - outputText.validate() - } - } - ) - validated = true - } + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("python") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + /** The image tag to use for the python script. */ + fun imageTag(imageTag: String) = imageTag(JsonField.of(imageTag)) - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitTextInput(textInput: String) = 1 + /** + * Sets [Builder.imageTag] to an arbitrary JSON value. + * + * You should usually call [Builder.imageTag] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun imageTag(imageTag: JsonField) = apply { this.imageTag = imageTag } - override fun visitResponseInputText( - responseInputText: ResponseInputText - ) = responseInputText.validity() + /** The threshold for the score. */ + fun passThreshold(passThreshold: Double) = + passThreshold(JsonField.of(passThreshold)) - override fun visitOutputText(outputText: OutputText) = - outputText.validity() + /** + * Sets [Builder.passThreshold] to an arbitrary JSON value. + * + * You should usually call [Builder.passThreshold] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun passThreshold(passThreshold: JsonField) = apply { + this.passThreshold = passThreshold + } - override fun unknown(json: JsonValue?) = 0 - } - ) + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - return /* spotless:off */ other is Content && textInput == other.textInput && responseInputText == other.responseInputText && outputText == other.outputText /* spotless:on */ + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(textInput, responseInputText, outputText) /* spotless:on */ - - override fun toString(): String = - when { - textInput != null -> "Content{textInput=$textInput}" - responseInputText != null -> - "Content{responseInputText=$responseInputText}" - outputText != null -> "Content{outputText=$outputText}" - _json != null -> "Content{_unknown=$_json}" - else -> throw IllegalStateException("Invalid Content") - } - - companion object { + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - /** A text input to the model. */ - @JvmStatic - fun ofTextInput(textInput: String) = Content(textInput = textInput) + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - /** A text input to the model. */ - @JvmStatic - fun ofResponseInputText(responseInputText: ResponseInputText) = - Content(responseInputText = responseInputText) + /** + * Returns an immutable instance of [EvalGraderPython]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .name() + * .source() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): EvalGraderPython = + EvalGraderPython( + checkRequired("name", name), + checkRequired("source", source), + type, + imageTag, + passThreshold, + additionalProperties.toMutableMap(), + ) + } - /** A text output from the model. */ - @JvmStatic - fun ofOutputText(outputText: OutputText) = Content(outputText = outputText) - } + private var validated: Boolean = false - /** - * An interface that defines how to map each variant of [Content] to a value of - * type [T]. - */ - interface Visitor { - - /** A text input to the model. */ - fun visitTextInput(textInput: String): T - - /** A text input to the model. */ - fun visitResponseInputText(responseInputText: ResponseInputText): T - - /** A text output from the model. */ - fun visitOutputText(outputText: OutputText): T - - /** - * Maps an unknown variant of [Content] to a value of type [T]. - * - * An instance of [Content] 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 Content: $json") - } - } + fun validate(): EvalGraderPython = apply { + if (validated) { + return@apply + } - internal class Deserializer : BaseDeserializer(Content::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): Content { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { Content(responseInputText = it, _json = json) }, - tryDeserialize(node, jacksonTypeRef())?.let { - Content(outputText = it, _json = json) - }, - tryDeserialize(node, jacksonTypeRef())?.let { - Content(textInput = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from array). - 0 -> Content(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } + name() + source() + _type().let { + if (it != JsonValue.from("python")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") } + } + imageTag() + passThreshold() + validated = true + } - internal class Serializer : BaseSerializer(Content::class) { - - override fun serialize( - value: Content, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.textInput != null -> generator.writeObject(value.textInput) - value.responseInputText != null -> - generator.writeObject(value.responseInputText) - value.outputText != null -> generator.writeObject(value.outputText) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid Content") - } - } - } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - /** A text output from the model. */ - class OutputText - private constructor( - private val text: JsonField, - private val type: JsonValue, - private val additionalProperties: MutableMap, - ) { + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (name.asKnown().isPresent) 1 else 0) + + (if (source.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("python")) 1 else 0 } + + (if (imageTag.asKnown().isPresent) 1 else 0) + + (if (passThreshold.asKnown().isPresent) 1 else 0) - @JsonCreator - private constructor( - @JsonProperty("text") - @ExcludeMissing - text: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - ) : this(text, type, mutableMapOf()) - - /** - * The text output from the model. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected - * type or is unexpectedly missing or null (e.g. if the server responded - * with an unexpected value). - */ - fun text(): String = text.getRequired("text") - - /** - * The type of the output text. Always `output_text`. - * - * Expected to always return the following: - * ```java - * JsonValue.from("output_text") - * ``` - * - * However, this method can be useful for debugging and logging (e.g. if the - * server responded with an unexpected value). - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - - /** - * Returns the raw JSON value of [text]. - * - * Unlike [text], this method doesn't throw if the JSON field has an - * unexpected type. - */ - @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { - - /** - * Returns a mutable builder for constructing an instance of - * [OutputText]. - * - * The following fields are required: - * ```java - * .text() - * ``` - */ - @JvmStatic fun builder() = Builder() - } + return /* spotless:off */ other is EvalGraderPython && name == other.name && source == other.source && type == other.type && imageTag == other.imageTag && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ + } - /** A builder for [OutputText]. */ - class Builder internal constructor() { - - private var text: JsonField? = null - private var type: JsonValue = JsonValue.from("output_text") - private var additionalProperties: MutableMap = - mutableMapOf() - - @JvmSynthetic - internal fun from(outputText: OutputText) = apply { - text = outputText.text - type = outputText.type - additionalProperties = - outputText.additionalProperties.toMutableMap() - } - - /** The text output from the model. */ - fun text(text: String) = text(JsonField.of(text)) - - /** - * Sets [Builder.text] to an arbitrary JSON value. - * - * You should usually call [Builder.text] with a well-typed [String] - * value instead. This method is primarily for setting the field to an - * undocumented or not yet supported value. - */ - fun text(text: JsonField) = apply { this.text = text } - - /** - * Sets the field to an arbitrary JSON value. - * - * It is usually unnecessary to call this method because the field - * defaults to the following: - * ```java - * JsonValue.from("output_text") - * ``` - * - * This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun type(type: JsonValue) = apply { this.type = type } - - 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) - } - - /** - * Returns an immutable instance of [OutputText]. - * - * Further updates to this [Builder] will not mutate the returned - * instance. - * - * The following fields are required: - * ```java - * .text() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): OutputText = - OutputText( - checkRequired("text", text), - type, - additionalProperties.toMutableMap(), - ) - } + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, additionalProperties) } + /* spotless:on */ - private var validated: Boolean = false - - fun validate(): OutputText = apply { - if (validated) { - return@apply - } - - text() - _type().let { - if (it != JsonValue.from("output_text")) { - throw OpenAIInvalidDataException( - "'type' is invalid, received $it" - ) - } - } - validated = true - } + override fun hashCode(): Int = hashCode - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this - * object recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (if (text.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("output_text")) 1 else 0 } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is OutputText && text == other.text && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ - } + override fun toString() = + "EvalGraderPython{name=$name, source=$source, type=$type, imageTag=$imageTag, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" + } - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(text, type, additionalProperties) } - /* spotless:on */ + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + class EvalGraderScoreModel + private constructor( + private val input: JsonField>, + private val model: JsonField, + private val name: JsonField, + private val type: JsonValue, + private val range: JsonField>, + private val samplingParams: JsonValue, + private val passThreshold: JsonField, + private val additionalProperties: MutableMap, + ) { - override fun hashCode(): Int = hashCode + @JsonCreator + private constructor( + @JsonProperty("input") + @ExcludeMissing + input: JsonField> = JsonMissing.of(), + @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("range") + @ExcludeMissing + range: JsonField> = JsonMissing.of(), + @JsonProperty("sampling_params") + @ExcludeMissing + samplingParams: JsonValue = JsonMissing.of(), + @JsonProperty("pass_threshold") + @ExcludeMissing + passThreshold: JsonField = JsonMissing.of(), + ) : this(input, model, name, type, range, samplingParams, passThreshold, mutableMapOf()) + + fun toScoreModelGrader(): ScoreModelGrader = + ScoreModelGrader.builder() + .input(input) + .model(model) + .name(name) + .type(type) + .range(range) + .samplingParams(samplingParams) + .build() - override fun toString() = - "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" - } - } + /** + * The input text. This may include template strings. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun input(): List = input.getRequired("input") - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - */ - class Role @JsonCreator private constructor(private val value: JsonField) : - Enum { + /** + * The model to use for the evaluation. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun model(): String = model.getRequired("model") - /** - * 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 + /** + * The name of the grader. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun name(): String = name.getRequired("name") - companion object { + /** + * The object type, which is always `score_model`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("score_model") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - @JvmField val USER = of("user") + /** + * The range of the score. Defaults to `[0, 1]`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun range(): Optional> = range.getOptional("range") - @JvmField val ASSISTANT = of("assistant") + /** The sampling parameters for the model. */ + @JsonProperty("sampling_params") + @ExcludeMissing + fun _samplingParams(): JsonValue = samplingParams - @JvmField val SYSTEM = of("system") + /** + * The threshold for the score. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") - @JvmField val DEVELOPER = of("developer") + /** + * Returns the raw JSON value of [input]. + * + * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("input") + @ExcludeMissing + fun _input(): JsonField> = input - @JvmStatic fun of(value: String) = Role(JsonField.of(value)) - } + /** + * Returns the raw JSON value of [model]. + * + * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model - /** An enum containing [Role]'s known values. */ - enum class Known { - USER, - ASSISTANT, - SYSTEM, - DEVELOPER, - } + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name - /** - * An enum containing [Role]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Role] 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 { - USER, - ASSISTANT, - SYSTEM, - DEVELOPER, - /** - * An enum member indicating that [Role] was instantiated with an unknown - * value. - */ - _UNKNOWN, - } + /** + * Returns the raw JSON value of [range]. + * + * Unlike [range], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("range") @ExcludeMissing fun _range(): JsonField> = range - /** - * 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) { - USER -> Value.USER - ASSISTANT -> Value.ASSISTANT - SYSTEM -> Value.SYSTEM - DEVELOPER -> Value.DEVELOPER - else -> Value._UNKNOWN - } + /** + * Returns the raw JSON value of [passThreshold]. + * + * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pass_threshold") + @ExcludeMissing + fun _passThreshold(): JsonField = passThreshold - /** - * 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) { - USER -> Known.USER - ASSISTANT -> Known.ASSISTANT - SYSTEM -> Known.SYSTEM - DEVELOPER -> Known.DEVELOPER - else -> throw OpenAIInvalidDataException("Unknown Role: $value") - } + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, 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") - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - private var validated: Boolean = false + fun toBuilder() = Builder().from(this) - fun validate(): Role = apply { - if (validated) { - return@apply - } + companion object { - known() - validated = true - } + /** + * Returns a mutable builder for constructing an instance of [EvalGraderScoreModel]. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + */ + @JvmStatic fun builder() = Builder() + } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + /** A builder for [EvalGraderScoreModel]. */ + class Builder internal constructor() { - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + private var input: JsonField>? = null + private var model: JsonField? = null + private var name: JsonField? = null + private var type: JsonValue = JsonValue.from("score_model") + private var range: JsonField>? = null + private var samplingParams: JsonValue = JsonMissing.of() + private var passThreshold: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + @JvmSynthetic + internal fun from(evalGraderScoreModel: EvalGraderScoreModel) = apply { + input = evalGraderScoreModel.input.map { it.toMutableList() } + model = evalGraderScoreModel.model + name = evalGraderScoreModel.name + type = evalGraderScoreModel.type + range = evalGraderScoreModel.range.map { it.toMutableList() } + samplingParams = evalGraderScoreModel.samplingParams + passThreshold = evalGraderScoreModel.passThreshold + additionalProperties = evalGraderScoreModel.additionalProperties.toMutableMap() + } - return /* spotless:off */ other is Role && value == other.value /* spotless:on */ - } + /** The input text. This may include template strings. */ + fun input(input: List) = input(JsonField.of(input)) - override fun hashCode() = value.hashCode() + /** + * Sets [Builder.input] to an arbitrary JSON value. + * + * You should usually call [Builder.input] with a well-typed + * `List` value instead. This method is primarily for + * setting the field to an undocumented or not yet supported value. + */ + fun input(input: JsonField>) = apply { + this.input = input.map { it.toMutableList() } + } - override fun toString() = value.toString() + /** + * Adds a single [ScoreModelGrader.Input] to [Builder.input]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addInput(input: ScoreModelGrader.Input) = apply { + this.input = + (this.input ?: JsonField.of(mutableListOf())).also { + checkKnown("input", it).add(input) + } } - /** The type of the message input. Always `message`. */ - class Type @JsonCreator private constructor(private val value: JsonField) : - Enum { + /** The model to use for the evaluation. */ + fun model(model: String) = model(JsonField.of(model)) - /** - * 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 + /** + * Sets [Builder.model] to an arbitrary JSON value. + * + * You should usually call [Builder.model] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun model(model: JsonField) = apply { this.model = model } - companion object { + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) - @JvmField val MESSAGE = of("message") + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun name(name: JsonField) = apply { this.name = name } - @JvmStatic fun of(value: String) = Type(JsonField.of(value)) - } + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("score_model") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - /** An enum containing [Type]'s known values. */ - enum class Known { - MESSAGE - } + /** The range of the score. Defaults to `[0, 1]`. */ + fun range(range: List) = range(JsonField.of(range)) - /** - * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Type] 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 { - MESSAGE, - /** - * An enum member indicating that [Type] was instantiated with an unknown - * value. - */ - _UNKNOWN, - } + /** + * Sets [Builder.range] to an arbitrary JSON value. + * + * You should usually call [Builder.range] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun range(range: JsonField>) = apply { + this.range = range.map { it.toMutableList() } + } - /** - * 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) { - MESSAGE -> Value.MESSAGE - else -> Value._UNKNOWN + /** + * Adds a single [Double] to [Builder.range]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addRange(range: Double) = apply { + this.range = + (this.range ?: JsonField.of(mutableListOf())).also { + checkKnown("range", it).add(range) } + } - /** - * 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) { - MESSAGE -> Known.MESSAGE - else -> throw OpenAIInvalidDataException("Unknown Type: $value") - } + /** The sampling parameters for the model. */ + fun samplingParams(samplingParams: JsonValue) = apply { + this.samplingParams = samplingParams + } - /** - * 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") - } + /** The threshold for the score. */ + fun passThreshold(passThreshold: Double) = + passThreshold(JsonField.of(passThreshold)) + + /** + * Sets [Builder.passThreshold] to an arbitrary JSON value. + * + * You should usually call [Builder.passThreshold] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun passThreshold(passThreshold: JsonField) = apply { + this.passThreshold = passThreshold + } - private var validated: Boolean = false + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - fun validate(): Type = apply { - if (validated) { - return@apply - } + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - known() - validated = true + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - return /* spotless:off */ other is Type && value == other.value /* spotless:on */ - } + /** + * Returns an immutable instance of [EvalGraderScoreModel]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): EvalGraderScoreModel = + EvalGraderScoreModel( + checkRequired("input", input).map { it.toImmutable() }, + checkRequired("model", model), + checkRequired("name", name), + type, + (range ?: JsonMissing.of()).map { it.toImmutable() }, + samplingParams, + passThreshold, + additionalProperties.toMutableMap(), + ) + } - override fun hashCode() = value.hashCode() + private var validated: Boolean = false - override fun toString() = value.toString() + fun validate(): EvalGraderScoreModel = apply { + if (validated) { + return@apply } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + input().forEach { it.validate() } + model() + name() + _type().let { + if (it != JsonValue.from("score_model")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") } - - return /* spotless:off */ other is Input && content == other.content && role == other.role && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ } + range() + passThreshold() + validated = true + } - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(content, role, type, additionalProperties) } - /* spotless:on */ - - override fun hashCode(): Int = hashCode + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - override fun toString() = - "Input{content=$content, role=$role, type=$type, additionalProperties=$additionalProperties}" - } + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (input.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (model.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("score_model")) 1 else 0 } + + (range.asKnown().getOrNull()?.size ?: 0) + + (if (passThreshold.asKnown().isPresent) 1 else 0) override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is ScoreModel && input == other.input && model == other.model && name == other.name && type == other.type && passThreshold == other.passThreshold && range == other.range && samplingParams == other.samplingParams && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is EvalGraderScoreModel && input == other.input && model == other.model && name == other.name && type == other.type && range == other.range && samplingParams == other.samplingParams && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(input, model, name, type, passThreshold, range, samplingParams, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(input, model, name, type, range, samplingParams, passThreshold, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "ScoreModel{input=$input, model=$model, name=$name, type=$type, passThreshold=$passThreshold, range=$range, samplingParams=$samplingParams, additionalProperties=$additionalProperties}" + "EvalGraderScoreModel{input=$input, model=$model, name=$name, type=$type, range=$range, samplingParams=$samplingParams, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalRetrieveParams.kt index 63111243..3a411a0b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalRetrieveParams.kt @@ -3,20 +3,21 @@ package com.openai.models.evals import com.openai.core.Params -import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Get an evaluation by ID. */ class EvalRetrieveParams private constructor( - private val evalId: String, + private val evalId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun evalId(): String = evalId + fun evalId(): Optional = Optional.ofNullable(evalId) fun _additionalHeaders(): Headers = additionalHeaders @@ -26,14 +27,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [EvalRetrieveParams]. - * - * The following fields are required: - * ```java - * .evalId() - * ``` - */ + @JvmStatic fun none(): EvalRetrieveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [EvalRetrieveParams]. */ @JvmStatic fun builder() = Builder() } @@ -51,7 +47,10 @@ private constructor( additionalQueryParams = evalRetrieveParams.additionalQueryParams.toBuilder() } - fun evalId(evalId: String) = apply { this.evalId = evalId } + fun evalId(evalId: String?) = apply { this.evalId = evalId } + + /** Alias for calling [Builder.evalId] with `evalId.orElse(null)`. */ + fun evalId(evalId: Optional) = evalId(evalId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -155,25 +154,14 @@ private constructor( * Returns an immutable instance of [EvalRetrieveParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .evalId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): EvalRetrieveParams = - EvalRetrieveParams( - checkRequired("evalId", evalId), - additionalHeaders.build(), - additionalQueryParams.build(), - ) + EvalRetrieveParams(evalId, additionalHeaders.build(), additionalQueryParams.build()) } fun _pathParam(index: Int): String = when (index) { - 0 -> evalId + 0 -> evalId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalRetrieveResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalRetrieveResponse.kt index f420c699..0cb33759 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalRetrieveResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalRetrieveResponse.kt @@ -15,7 +15,6 @@ 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.Enum import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing @@ -26,7 +25,11 @@ import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException -import com.openai.models.responses.ResponseInputText +import com.openai.models.graders.gradermodels.LabelModelGrader +import com.openai.models.graders.gradermodels.PythonGrader +import com.openai.models.graders.gradermodels.ScoreModelGrader +import com.openai.models.graders.gradermodels.StringCheckGrader +import com.openai.models.graders.gradermodels.TextSimilarityGrader import java.util.Collections import java.util.Objects import java.util.Optional @@ -37,7 +40,7 @@ import kotlin.jvm.optionals.getOrNull * done for your LLM integration. Like: * - Improve the quality of my chatbot * - See how well my chatbot handles customer support - * - Check if o3-mini is better at my usecase than gpt-4o + * - Check if o4-mini is better at my usecase than gpt-4o */ class EvalRetrieveResponse private constructor( @@ -294,10 +297,26 @@ private constructor( fun customDataSourceConfig(schema: EvalCustomDataSourceConfig.Schema) = dataSourceConfig(EvalCustomDataSourceConfig.builder().schema(schema).build()) + /** Alias for calling [dataSourceConfig] with `DataSourceConfig.ofLogs(logs)`. */ + fun dataSourceConfig(logs: DataSourceConfig.Logs) = + dataSourceConfig(DataSourceConfig.ofLogs(logs)) + + /** + * Alias for calling [dataSourceConfig] with the following: + * ```java + * DataSourceConfig.Logs.builder() + * .schema(schema) + * .build() + * ``` + */ + fun logsDataSourceConfig(schema: DataSourceConfig.Logs.Schema) = + dataSourceConfig(DataSourceConfig.Logs.builder().schema(schema).build()) + /** * Alias for calling [dataSourceConfig] with * `DataSourceConfig.ofStoredCompletions(storedCompletions)`. */ + @Deprecated("deprecated") fun dataSourceConfig(storedCompletions: EvalStoredCompletionsDataSourceConfig) = dataSourceConfig(DataSourceConfig.ofStoredCompletions(storedCompletions)) @@ -309,6 +328,7 @@ private constructor( * .build() * ``` */ + @Deprecated("deprecated") fun storedCompletionsDataSourceConfig( schema: EvalStoredCompletionsDataSourceConfig.Schema ) = dataSourceConfig(EvalStoredCompletionsDataSourceConfig.builder().schema(schema).build()) @@ -388,34 +408,43 @@ private constructor( } /** - * Alias for calling [addTestingCriterion] with `TestingCriterion.ofLabelModel(labelModel)`. + * Alias for calling [addTestingCriterion] with + * `TestingCriterion.ofLabelModelGrader(labelModelGrader)`. */ - fun addTestingCriterion(labelModel: EvalLabelModelGrader) = - addTestingCriterion(TestingCriterion.ofLabelModel(labelModel)) + fun addTestingCriterion(labelModelGrader: LabelModelGrader) = + addTestingCriterion(TestingCriterion.ofLabelModelGrader(labelModelGrader)) /** * Alias for calling [addTestingCriterion] with - * `TestingCriterion.ofStringCheck(stringCheck)`. + * `TestingCriterion.ofStringCheckGrader(stringCheckGrader)`. */ - fun addTestingCriterion(stringCheck: EvalStringCheckGrader) = - addTestingCriterion(TestingCriterion.ofStringCheck(stringCheck)) + fun addTestingCriterion(stringCheckGrader: StringCheckGrader) = + addTestingCriterion(TestingCriterion.ofStringCheckGrader(stringCheckGrader)) /** * Alias for calling [addTestingCriterion] with - * `TestingCriterion.ofTextSimilarity(textSimilarity)`. + * `TestingCriterion.ofEvalGraderTextSimilarity(evalGraderTextSimilarity)`. */ - fun addTestingCriterion(textSimilarity: EvalTextSimilarityGrader) = - addTestingCriterion(TestingCriterion.ofTextSimilarity(textSimilarity)) + fun addTestingCriterion( + evalGraderTextSimilarity: TestingCriterion.EvalGraderTextSimilarity + ) = + addTestingCriterion( + TestingCriterion.ofEvalGraderTextSimilarity(evalGraderTextSimilarity) + ) - /** Alias for calling [addTestingCriterion] with `TestingCriterion.ofPython(python)`. */ - fun addTestingCriterion(python: TestingCriterion.Python) = - addTestingCriterion(TestingCriterion.ofPython(python)) + /** + * Alias for calling [addTestingCriterion] with + * `TestingCriterion.ofEvalGraderPython(evalGraderPython)`. + */ + fun addTestingCriterion(evalGraderPython: TestingCriterion.EvalGraderPython) = + addTestingCriterion(TestingCriterion.ofEvalGraderPython(evalGraderPython)) /** - * Alias for calling [addTestingCriterion] with `TestingCriterion.ofScoreModel(scoreModel)`. + * Alias for calling [addTestingCriterion] with + * `TestingCriterion.ofEvalGraderScoreModel(evalGraderScoreModel)`. */ - fun addTestingCriterion(scoreModel: TestingCriterion.ScoreModel) = - addTestingCriterion(TestingCriterion.ofScoreModel(scoreModel)) + fun addTestingCriterion(evalGraderScoreModel: TestingCriterion.EvalGraderScoreModel) = + addTestingCriterion(TestingCriterion.ofEvalGraderScoreModel(evalGraderScoreModel)) fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -516,6 +545,7 @@ private constructor( class DataSourceConfig private constructor( private val custom: EvalCustomDataSourceConfig? = null, + private val logs: Logs? = null, private val storedCompletions: EvalStoredCompletionsDataSourceConfig? = null, private val _json: JsonValue? = null, ) { @@ -529,18 +559,23 @@ private constructor( fun custom(): Optional = Optional.ofNullable(custom) /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your stored - * completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both defined - * when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This is + * usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema returned + * by this data source config is used to defined what variables are available in your evals. + * `item` and `sample` are both defined when using this data source config. */ + fun logs(): Optional = Optional.ofNullable(logs) + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") fun storedCompletions(): Optional = Optional.ofNullable(storedCompletions) fun isCustom(): Boolean = custom != null - fun isStoredCompletions(): Boolean = storedCompletions != null + fun isLogs(): Boolean = logs != null + + @Deprecated("deprecated") fun isStoredCompletions(): Boolean = storedCompletions != null /** * A CustomDataSourceConfig which specifies the schema of your `item` and optionally @@ -551,12 +586,15 @@ private constructor( fun asCustom(): EvalCustomDataSourceConfig = custom.getOrThrow("custom") /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your stored - * completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both defined - * when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This is + * usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema returned + * by this data source config is used to defined what variables are available in your evals. + * `item` and `sample` are both defined when using this data source config. */ + fun asLogs(): Logs = logs.getOrThrow("logs") + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") fun asStoredCompletions(): EvalStoredCompletionsDataSourceConfig = storedCompletions.getOrThrow("storedCompletions") @@ -565,6 +603,7 @@ private constructor( fun accept(visitor: Visitor): T = when { custom != null -> visitor.visitCustom(custom) + logs != null -> visitor.visitLogs(logs) storedCompletions != null -> visitor.visitStoredCompletions(storedCompletions) else -> visitor.unknown(_json) } @@ -582,6 +621,10 @@ private constructor( custom.validate() } + override fun visitLogs(logs: Logs) { + logs.validate() + } + override fun visitStoredCompletions( storedCompletions: EvalStoredCompletionsDataSourceConfig ) { @@ -612,6 +655,8 @@ private constructor( object : Visitor { override fun visitCustom(custom: EvalCustomDataSourceConfig) = custom.validity() + override fun visitLogs(logs: Logs) = logs.validity() + override fun visitStoredCompletions( storedCompletions: EvalStoredCompletionsDataSourceConfig ) = storedCompletions.validity() @@ -625,14 +670,15 @@ private constructor( return true } - return /* spotless:off */ other is DataSourceConfig && custom == other.custom && storedCompletions == other.storedCompletions /* spotless:on */ + return /* spotless:off */ other is DataSourceConfig && custom == other.custom && logs == other.logs && storedCompletions == other.storedCompletions /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(custom, storedCompletions) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(custom, logs, storedCompletions) /* spotless:on */ override fun toString(): String = when { custom != null -> "DataSourceConfig{custom=$custom}" + logs != null -> "DataSourceConfig{logs=$logs}" storedCompletions != null -> "DataSourceConfig{storedCompletions=$storedCompletions}" _json != null -> "DataSourceConfig{_unknown=$_json}" @@ -651,12 +697,16 @@ private constructor( fun ofCustom(custom: EvalCustomDataSourceConfig) = DataSourceConfig(custom = custom) /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your - * stored completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both - * defined when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This + * is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema + * returned by this data source config is used to defined what variables are available + * in your evals. `item` and `sample` are both defined when using this data source + * config. */ + @JvmStatic fun ofLogs(logs: Logs) = DataSourceConfig(logs = logs) + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") @JvmStatic fun ofStoredCompletions(storedCompletions: EvalStoredCompletionsDataSourceConfig) = DataSourceConfig(storedCompletions = storedCompletions) @@ -677,12 +727,16 @@ private constructor( fun visitCustom(custom: EvalCustomDataSourceConfig): T /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your - * stored completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both - * defined when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This + * is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema + * returned by this data source config is used to defined what variables are available + * in your evals. `item` and `sample` are both defined when using this data source + * config. */ + fun visitLogs(logs: Logs): T + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") fun visitStoredCompletions(storedCompletions: EvalStoredCompletionsDataSourceConfig): T /** @@ -712,6 +766,11 @@ private constructor( ?.let { DataSourceConfig(custom = it, _json = json) } ?: DataSourceConfig(_json = json) } + "logs" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DataSourceConfig(logs = it, _json = json) + } ?: DataSourceConfig(_json = json) + } "stored_completions" -> { return tryDeserialize( node, @@ -735,6 +794,7 @@ private constructor( ) { when { value.custom != null -> generator.writeObject(value.custom) + value.logs != null -> generator.writeObject(value.logs) value.storedCompletions != null -> generator.writeObject(value.storedCompletions) value._json != null -> generator.writeObject(value._json) @@ -742,787 +802,990 @@ private constructor( } } } - } - - /** - * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing - * additional information about the object in a structured format, and querying for objects via - * API or the dashboard. - * - * Keys are strings with a maximum length of 64 characters. Values are strings with a maximum - * length of 512 characters. - */ - class Metadata - @JsonCreator - private constructor( - @com.fasterxml.jackson.annotation.JsonValue - private val additionalProperties: Map - ) { - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = additionalProperties - - fun toBuilder() = Builder().from(this) - - companion object { - - /** Returns a mutable builder for constructing an instance of [Metadata]. */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [Metadata]. */ - class Builder internal constructor() { - - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(metadata: Metadata) = apply { - additionalProperties = metadata.additionalProperties.toMutableMap() - } - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + /** + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This is + * usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema returned + * by this data source config is used to defined what variables are available in your evals. + * `item` and `sample` are both defined when using this data source config. + */ + class Logs + private constructor( + private val schema: JsonField, + private val type: JsonValue, + private val metadata: JsonField, + private val additionalProperties: MutableMap, + ) { - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + @JsonCreator + private constructor( + @JsonProperty("schema") + @ExcludeMissing + schema: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("metadata") + @ExcludeMissing + metadata: JsonField = JsonMissing.of(), + ) : this(schema, type, metadata, mutableMapOf()) - fun putAllAdditionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.putAll(additionalProperties) - } + /** + * The json schema for the run data source items. Learn how to build JSON schemas + * [here](https://json-schema.org/). + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun schema(): Schema = schema.getRequired("schema") - fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + /** + * The type of data source. Always `logs`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("logs") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** + * Set of 16 key-value pairs that can be attached to an object. This can be useful for + * storing additional information about the object in a structured format, and querying + * for objects via API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with a + * maximum length of 512 characters. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun metadata(): Optional = metadata.getOptional("metadata") /** - * Returns an immutable instance of [Metadata]. + * Returns the raw JSON value of [schema]. * - * Further updates to this [Builder] will not mutate the returned instance. + * Unlike [schema], this method doesn't throw if the JSON field has an unexpected type. */ - fun build(): Metadata = Metadata(additionalProperties.toImmutable()) - } + @JsonProperty("schema") @ExcludeMissing fun _schema(): JsonField = schema - private var validated: Boolean = false + /** + * Returns the raw JSON value of [metadata]. + * + * Unlike [metadata], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("metadata") + @ExcludeMissing + fun _metadata(): JsonField = metadata - fun validate(): Metadata = apply { - if (validated) { - return@apply + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) } - validated = true - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + fun toBuilder() = Builder().from(this) - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + companion object { - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + /** + * Returns a mutable builder for constructing an instance of [Logs]. + * + * The following fields are required: + * ```java + * .schema() + * ``` + */ + @JvmStatic fun builder() = Builder() } - return /* spotless:off */ other is Metadata && additionalProperties == other.additionalProperties /* spotless:on */ - } + /** A builder for [Logs]. */ + class Builder internal constructor() { - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(additionalProperties) } - /* spotless:on */ + private var schema: JsonField? = null + private var type: JsonValue = JsonValue.from("logs") + private var metadata: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() - override fun hashCode(): Int = hashCode + @JvmSynthetic + internal fun from(logs: Logs) = apply { + schema = logs.schema + type = logs.type + metadata = logs.metadata + additionalProperties = logs.additionalProperties.toMutableMap() + } - override fun toString() = "Metadata{additionalProperties=$additionalProperties}" - } + /** + * The json schema for the run data source items. Learn how to build JSON schemas + * [here](https://json-schema.org/). + */ + fun schema(schema: Schema) = schema(JsonField.of(schema)) - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the evaluation. - */ - @JsonDeserialize(using = TestingCriterion.Deserializer::class) - @JsonSerialize(using = TestingCriterion.Serializer::class) - class TestingCriterion - private constructor( - private val labelModel: EvalLabelModelGrader? = null, - private val stringCheck: EvalStringCheckGrader? = null, - private val textSimilarity: EvalTextSimilarityGrader? = null, - private val python: Python? = null, - private val scoreModel: ScoreModel? = null, - private val _json: JsonValue? = null, - ) { + /** + * Sets [Builder.schema] to an arbitrary JSON value. + * + * You should usually call [Builder.schema] with a well-typed [Schema] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun schema(schema: JsonField) = apply { this.schema = schema } - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - fun labelModel(): Optional = Optional.ofNullable(labelModel) + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("logs") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - /** - * A StringCheckGrader object that performs a string comparison between input and reference - * using a specified operation. - */ - fun stringCheck(): Optional = Optional.ofNullable(stringCheck) + /** + * Set of 16 key-value pairs that can be attached to an object. This can be useful + * for storing additional information about the object in a structured format, and + * querying for objects via API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with + * a maximum length of 512 characters. + */ + fun metadata(metadata: Metadata?) = metadata(JsonField.ofNullable(metadata)) - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun textSimilarity(): Optional = - Optional.ofNullable(textSimilarity) + /** Alias for calling [Builder.metadata] with `metadata.orElse(null)`. */ + fun metadata(metadata: Optional) = metadata(metadata.getOrNull()) - /** A PythonGrader object that runs a python script on the input. */ - fun python(): Optional = Optional.ofNullable(python) + /** + * Sets [Builder.metadata] to an arbitrary JSON value. + * + * You should usually call [Builder.metadata] with a well-typed [Metadata] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun metadata(metadata: JsonField) = apply { this.metadata = metadata } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun scoreModel(): Optional = Optional.ofNullable(scoreModel) + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - fun isLabelModel(): Boolean = labelModel != null + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - fun isStringCheck(): Boolean = stringCheck != null + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } - fun isTextSimilarity(): Boolean = textSimilarity != null + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - fun isPython(): Boolean = python != null + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - fun isScoreModel(): Boolean = scoreModel != null + /** + * Returns an immutable instance of [Logs]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .schema() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Logs = + Logs( + checkRequired("schema", schema), + type, + metadata, + additionalProperties.toMutableMap(), + ) + } - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - fun asLabelModel(): EvalLabelModelGrader = labelModel.getOrThrow("labelModel") + private var validated: Boolean = false - /** - * A StringCheckGrader object that performs a string comparison between input and reference - * using a specified operation. - */ - fun asStringCheck(): EvalStringCheckGrader = stringCheck.getOrThrow("stringCheck") + fun validate(): Logs = apply { + if (validated) { + return@apply + } - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun asTextSimilarity(): EvalTextSimilarityGrader = - textSimilarity.getOrThrow("textSimilarity") + schema().validate() + _type().let { + if (it != JsonValue.from("logs")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + metadata().ifPresent { it.validate() } + validated = true + } - /** A PythonGrader object that runs a python script on the input. */ - fun asPython(): Python = python.getOrThrow("python") + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun asScoreModel(): ScoreModel = scoreModel.getOrThrow("scoreModel") + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (schema.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("logs")) 1 else 0 } + + (metadata.asKnown().getOrNull()?.validity() ?: 0) - fun _json(): Optional = Optional.ofNullable(_json) + /** + * The json schema for the run data source items. Learn how to build JSON schemas + * [here](https://json-schema.org/). + */ + class Schema + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { - fun accept(visitor: Visitor): T = - when { - labelModel != null -> visitor.visitLabelModel(labelModel) - stringCheck != null -> visitor.visitStringCheck(stringCheck) - textSimilarity != null -> visitor.visitTextSimilarity(textSimilarity) - python != null -> visitor.visitPython(python) - scoreModel != null -> visitor.visitScoreModel(scoreModel) - else -> visitor.unknown(_json) - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties - private var validated: Boolean = false + fun toBuilder() = Builder().from(this) - fun validate(): TestingCriterion = apply { - if (validated) { - return@apply - } + companion object { - accept( - object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) { - labelModel.validate() + /** Returns a mutable builder for constructing an instance of [Schema]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Schema]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(schema: Schema) = apply { + additionalProperties = schema.additionalProperties.toMutableMap() } - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) { - stringCheck.validate() + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) } - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) { - textSimilarity.validate() + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) } - override fun visitPython(python: Python) { - python.validate() + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) } - override fun visitScoreModel(scoreModel: ScoreModel) { - scoreModel.validate() + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) } + + /** + * Returns an immutable instance of [Schema]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Schema = Schema(additionalProperties.toImmutable()) } - ) - validated = true - } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + private var validated: Boolean = false - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) = - labelModel.validity() + fun validate(): Schema = apply { + if (validated) { + return@apply + } - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) = - stringCheck.validity() + validated = true + } - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - textSimilarity.validity() + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - override fun visitPython(python: Python) = python.validity() + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> + !value.isNull() && !value.isMissing() + } - override fun visitScoreModel(scoreModel: ScoreModel) = scoreModel.validity() + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - override fun unknown(json: JsonValue?) = 0 + return /* spotless:off */ other is Schema && additionalProperties == other.additionalProperties /* spotless:on */ } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - return /* spotless:off */ other is TestingCriterion && labelModel == other.labelModel && stringCheck == other.stringCheck && textSimilarity == other.textSimilarity && python == other.python && scoreModel == other.scoreModel /* spotless:on */ - } + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ - override fun hashCode(): Int = /* spotless:off */ Objects.hash(labelModel, stringCheck, textSimilarity, python, scoreModel) /* spotless:on */ + override fun hashCode(): Int = hashCode - override fun toString(): String = - when { - labelModel != null -> "TestingCriterion{labelModel=$labelModel}" - stringCheck != null -> "TestingCriterion{stringCheck=$stringCheck}" - textSimilarity != null -> "TestingCriterion{textSimilarity=$textSimilarity}" - python != null -> "TestingCriterion{python=$python}" - scoreModel != null -> "TestingCriterion{scoreModel=$scoreModel}" - _json != null -> "TestingCriterion{_unknown=$_json}" - else -> throw IllegalStateException("Invalid TestingCriterion") + override fun toString() = "Schema{additionalProperties=$additionalProperties}" } - companion object { - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. + * Set of 16 key-value pairs that can be attached to an object. This can be useful for + * storing additional information about the object in a structured format, and querying + * for objects via API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with a + * maximum length of 512 characters. */ - @JvmStatic - fun ofLabelModel(labelModel: EvalLabelModelGrader) = - TestingCriterion(labelModel = labelModel) + class Metadata + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { - /** - * A StringCheckGrader object that performs a string comparison between input and - * reference using a specified operation. - */ - @JvmStatic - fun ofStringCheck(stringCheck: EvalStringCheckGrader) = - TestingCriterion(stringCheck = stringCheck) + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - @JvmStatic - fun ofTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - TestingCriterion(textSimilarity = textSimilarity) + fun toBuilder() = Builder().from(this) - /** A PythonGrader object that runs a python script on the input. */ - @JvmStatic fun ofPython(python: Python) = TestingCriterion(python = python) + companion object { - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - @JvmStatic - fun ofScoreModel(scoreModel: ScoreModel) = TestingCriterion(scoreModel = scoreModel) - } + /** Returns a mutable builder for constructing an instance of [Metadata]. */ + @JvmStatic fun builder() = Builder() + } - /** - * An interface that defines how to map each variant of [TestingCriterion] to a value of - * type [T]. - */ - interface Visitor { + /** A builder for [Metadata]. */ + class Builder internal constructor() { - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - fun visitLabelModel(labelModel: EvalLabelModelGrader): T + private var additionalProperties: MutableMap = mutableMapOf() - /** - * A StringCheckGrader object that performs a string comparison between input and - * reference using a specified operation. - */ - fun visitStringCheck(stringCheck: EvalStringCheckGrader): T + @JvmSynthetic + internal fun from(metadata: Metadata) = apply { + additionalProperties = metadata.additionalProperties.toMutableMap() + } - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader): T + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - /** A PythonGrader object that runs a python script on the input. */ - fun visitPython(python: Python): T + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun visitScoreModel(scoreModel: ScoreModel): T + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } - /** - * Maps an unknown variant of [TestingCriterion] to a value of type [T]. - * - * An instance of [TestingCriterion] 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 TestingCriterion: $json") - } - } + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - internal class Deserializer : BaseDeserializer(TestingCriterion::class) { + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - override fun ObjectCodec.deserialize(node: JsonNode): TestingCriterion { - val json = JsonValue.fromJsonNode(node) - val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() + /** + * Returns an immutable instance of [Metadata]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Metadata = Metadata(additionalProperties.toImmutable()) + } - when (type) { - "label_model" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(labelModel = it, _json = json) - } ?: TestingCriterion(_json = json) - } - "string_check" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(stringCheck = it, _json = json) - } ?: TestingCriterion(_json = json) + private var validated: Boolean = false + + fun validate(): Metadata = apply { + if (validated) { + return@apply } - "text_similarity" -> { - return tryDeserialize(node, jacksonTypeRef()) - ?.let { TestingCriterion(textSimilarity = it, _json = json) } - ?: TestingCriterion(_json = json) + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false } - "python" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(python = it, _json = json) - } ?: TestingCriterion(_json = json) + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> + !value.isNull() && !value.isMissing() } - "score_model" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(scoreModel = it, _json = json) - } ?: TestingCriterion(_json = json) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } + + return /* spotless:off */ other is Metadata && additionalProperties == other.additionalProperties /* spotless:on */ } - return TestingCriterion(_json = json) - } - } + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ - internal class Serializer : BaseSerializer(TestingCriterion::class) { + override fun hashCode(): Int = hashCode - override fun serialize( - value: TestingCriterion, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.labelModel != null -> generator.writeObject(value.labelModel) - value.stringCheck != null -> generator.writeObject(value.stringCheck) - value.textSimilarity != null -> generator.writeObject(value.textSimilarity) - value.python != null -> generator.writeObject(value.python) - value.scoreModel != null -> generator.writeObject(value.scoreModel) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid TestingCriterion") + override fun toString() = "Metadata{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } + + return /* spotless:off */ other is Logs && schema == other.schema && type == other.type && metadata == other.metadata && additionalProperties == other.additionalProperties /* spotless:on */ } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(schema, type, metadata, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Logs{schema=$schema, type=$type, metadata=$metadata, additionalProperties=$additionalProperties}" } + } - /** A PythonGrader object that runs a python script on the input. */ - class Python - private constructor( - private val name: JsonField, - private val source: JsonField, - private val type: JsonValue, - private val imageTag: JsonField, - private val passThreshold: JsonField, - private val additionalProperties: MutableMap, - ) { + /** + * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing + * additional information about the object in a structured format, and querying for objects via + * API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with a maximum + * length of 512 characters. + */ + class Metadata + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { - @JsonCreator - private constructor( - @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), - @JsonProperty("source") - @ExcludeMissing - source: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - @JsonProperty("image_tag") - @ExcludeMissing - imageTag: JsonField = JsonMissing.of(), - @JsonProperty("pass_threshold") - @ExcludeMissing - passThreshold: JsonField = JsonMissing.of(), - ) : this(name, source, type, imageTag, passThreshold, mutableMapOf()) + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties - /** - * The name of the grader. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun name(): String = name.getRequired("name") + fun toBuilder() = Builder().from(this) - /** - * The source code of the python script. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun source(): String = source.getRequired("source") + companion object { - /** - * The object type, which is always `python`. - * - * Expected to always return the following: - * ```java - * JsonValue.from("python") - * ``` - * - * However, this method can be useful for debugging and logging (e.g. if the server - * responded with an unexpected value). - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + /** Returns a mutable builder for constructing an instance of [Metadata]. */ + @JvmStatic fun builder() = Builder() + } - /** - * The image tag to use for the python script. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun imageTag(): Optional = imageTag.getOptional("image_tag") + /** A builder for [Metadata]. */ + class Builder internal constructor() { - /** - * The threshold for the score. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + private var additionalProperties: MutableMap = mutableMapOf() - /** - * Returns the raw JSON value of [name]. - * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JvmSynthetic + internal fun from(metadata: Metadata) = apply { + additionalProperties = metadata.additionalProperties.toMutableMap() + } - /** - * Returns the raw JSON value of [source]. - * - * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - /** - * Returns the raw JSON value of [imageTag]. - * - * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag + 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) + } /** - * Returns the raw JSON value of [passThreshold]. + * Returns an immutable instance of [Metadata]. * - * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected - * type. + * Further updates to this [Builder] will not mutate the returned instance. */ - @JsonProperty("pass_threshold") - @ExcludeMissing - fun _passThreshold(): JsonField = passThreshold + fun build(): Metadata = Metadata(additionalProperties.toImmutable()) + } - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) + private var validated: Boolean = false + + fun validate(): Metadata = apply { + if (validated) { + return@apply } - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) + validated = true + } - fun toBuilder() = Builder().from(this) + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - companion object { + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } - /** - * Returns a mutable builder for constructing an instance of [Python]. - * - * The following fields are required: - * ```java - * .name() - * .source() - * ``` - */ - @JvmStatic fun builder() = Builder() + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } - /** A builder for [Python]. */ - class Builder internal constructor() { + return /* spotless:off */ other is Metadata && additionalProperties == other.additionalProperties /* spotless:on */ + } - private var name: JsonField? = null - private var source: JsonField? = null - private var type: JsonValue = JsonValue.from("python") - private var imageTag: JsonField = JsonMissing.of() - private var passThreshold: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ - @JvmSynthetic - internal fun from(python: Python) = apply { - name = python.name - source = python.source - type = python.type - imageTag = python.imageTag - passThreshold = python.passThreshold - additionalProperties = python.additionalProperties.toMutableMap() - } + override fun hashCode(): Int = hashCode - /** The name of the grader. */ - fun name(name: String) = name(JsonField.of(name)) + override fun toString() = "Metadata{additionalProperties=$additionalProperties}" + } - /** - * Sets [Builder.name] to an arbitrary JSON value. - * - * You should usually call [Builder.name] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun name(name: JsonField) = apply { this.name = name } + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the evaluation. + */ + @JsonDeserialize(using = TestingCriterion.Deserializer::class) + @JsonSerialize(using = TestingCriterion.Serializer::class) + class TestingCriterion + private constructor( + private val labelModelGrader: LabelModelGrader? = null, + private val stringCheckGrader: StringCheckGrader? = null, + private val evalGraderTextSimilarity: EvalGraderTextSimilarity? = null, + private val evalGraderPython: EvalGraderPython? = null, + private val evalGraderScoreModel: EvalGraderScoreModel? = null, + private val _json: JsonValue? = null, + ) { - /** The source code of the python script. */ - fun source(source: String) = source(JsonField.of(source)) + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + fun labelModelGrader(): Optional = Optional.ofNullable(labelModelGrader) - /** - * Sets [Builder.source] to an arbitrary JSON value. - * - * You should usually call [Builder.source] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun source(source: JsonField) = apply { this.source = source } + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun stringCheckGrader(): Optional = + Optional.ofNullable(stringCheckGrader) - /** - * Sets the field to an arbitrary JSON value. - * - * It is usually unnecessary to call this method because the field defaults to the - * following: - * ```java - * JsonValue.from("python") - * ``` - * - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun type(type: JsonValue) = apply { this.type = type } + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun evalGraderTextSimilarity(): Optional = + Optional.ofNullable(evalGraderTextSimilarity) - /** The image tag to use for the python script. */ - fun imageTag(imageTag: String) = imageTag(JsonField.of(imageTag)) + /** A PythonGrader object that runs a python script on the input. */ + fun evalGraderPython(): Optional = Optional.ofNullable(evalGraderPython) - /** - * Sets [Builder.imageTag] to an arbitrary JSON value. - * - * You should usually call [Builder.imageTag] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun imageTag(imageTag: JsonField) = apply { this.imageTag = imageTag } + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun evalGraderScoreModel(): Optional = + Optional.ofNullable(evalGraderScoreModel) - /** The threshold for the score. */ - fun passThreshold(passThreshold: Double) = - passThreshold(JsonField.of(passThreshold)) + fun isLabelModelGrader(): Boolean = labelModelGrader != null - /** - * Sets [Builder.passThreshold] to an arbitrary JSON value. - * - * You should usually call [Builder.passThreshold] with a well-typed [Double] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun passThreshold(passThreshold: JsonField) = apply { - this.passThreshold = passThreshold - } + fun isStringCheckGrader(): Boolean = stringCheckGrader != null - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + fun isEvalGraderTextSimilarity(): Boolean = evalGraderTextSimilarity != null - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + fun isEvalGraderPython(): Boolean = evalGraderPython != null - fun putAllAdditionalProperties(additionalProperties: Map) = - apply { - this.additionalProperties.putAll(additionalProperties) - } + fun isEvalGraderScoreModel(): Boolean = evalGraderScoreModel != null - fun removeAdditionalProperty(key: String) = apply { - additionalProperties.remove(key) - } + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + fun asLabelModelGrader(): LabelModelGrader = labelModelGrader.getOrThrow("labelModelGrader") - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun asStringCheckGrader(): StringCheckGrader = + stringCheckGrader.getOrThrow("stringCheckGrader") - /** - * Returns an immutable instance of [Python]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .name() - * .source() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): Python = - Python( - checkRequired("name", name), - checkRequired("source", source), - type, - imageTag, - passThreshold, - additionalProperties.toMutableMap(), - ) + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun asEvalGraderTextSimilarity(): EvalGraderTextSimilarity = + evalGraderTextSimilarity.getOrThrow("evalGraderTextSimilarity") + + /** A PythonGrader object that runs a python script on the input. */ + fun asEvalGraderPython(): EvalGraderPython = evalGraderPython.getOrThrow("evalGraderPython") + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun asEvalGraderScoreModel(): EvalGraderScoreModel = + evalGraderScoreModel.getOrThrow("evalGraderScoreModel") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + labelModelGrader != null -> visitor.visitLabelModelGrader(labelModelGrader) + stringCheckGrader != null -> visitor.visitStringCheckGrader(stringCheckGrader) + evalGraderTextSimilarity != null -> + visitor.visitEvalGraderTextSimilarity(evalGraderTextSimilarity) + evalGraderPython != null -> visitor.visitEvalGraderPython(evalGraderPython) + evalGraderScoreModel != null -> + visitor.visitEvalGraderScoreModel(evalGraderScoreModel) + else -> visitor.unknown(_json) } - private var validated: Boolean = false + private var validated: Boolean = false - fun validate(): Python = apply { - if (validated) { - return@apply - } + fun validate(): TestingCriterion = apply { + if (validated) { + return@apply + } - name() - source() - _type().let { - if (it != JsonValue.from("python")) { - throw OpenAIInvalidDataException("'type' is invalid, received $it") + accept( + object : Visitor { + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) { + labelModelGrader.validate() + } + + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) { + stringCheckGrader.validate() + } + + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) { + evalGraderTextSimilarity.validate() + } + + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) { + evalGraderPython.validate() + } + + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) { + evalGraderScoreModel.validate() } } - imageTag() - passThreshold() - validated = true + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) = + labelModelGrader.validity() + + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) = + stringCheckGrader.validity() + + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) = evalGraderTextSimilarity.validity() + + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) = + evalGraderPython.validity() + + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) = evalGraderScoreModel.validity() + + override fun unknown(json: JsonValue?) = 0 } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is TestingCriterion && labelModelGrader == other.labelModelGrader && stringCheckGrader == other.stringCheckGrader && evalGraderTextSimilarity == other.evalGraderTextSimilarity && evalGraderPython == other.evalGraderPython && evalGraderScoreModel == other.evalGraderScoreModel /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(labelModelGrader, stringCheckGrader, evalGraderTextSimilarity, evalGraderPython, evalGraderScoreModel) /* spotless:on */ + + override fun toString(): String = + when { + labelModelGrader != null -> "TestingCriterion{labelModelGrader=$labelModelGrader}" + stringCheckGrader != null -> + "TestingCriterion{stringCheckGrader=$stringCheckGrader}" + evalGraderTextSimilarity != null -> + "TestingCriterion{evalGraderTextSimilarity=$evalGraderTextSimilarity}" + evalGraderPython != null -> "TestingCriterion{evalGraderPython=$evalGraderPython}" + evalGraderScoreModel != null -> + "TestingCriterion{evalGraderScoreModel=$evalGraderScoreModel}" + _json != null -> "TestingCriterion{_unknown=$_json}" + else -> throw IllegalStateException("Invalid TestingCriterion") + } + + companion object { + + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + @JvmStatic + fun ofLabelModelGrader(labelModelGrader: LabelModelGrader) = + TestingCriterion(labelModelGrader = labelModelGrader) + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + @JvmStatic + fun ofStringCheckGrader(stringCheckGrader: StringCheckGrader) = + TestingCriterion(stringCheckGrader = stringCheckGrader) + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + @JvmStatic + fun ofEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity) = + TestingCriterion(evalGraderTextSimilarity = evalGraderTextSimilarity) + + /** A PythonGrader object that runs a python script on the input. */ + @JvmStatic + fun ofEvalGraderPython(evalGraderPython: EvalGraderPython) = + TestingCriterion(evalGraderPython = evalGraderPython) + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + @JvmStatic + fun ofEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel) = + TestingCriterion(evalGraderScoreModel = evalGraderScoreModel) + } + + /** + * An interface that defines how to map each variant of [TestingCriterion] to a value of + * type [T]. + */ + interface Visitor { + + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + fun visitLabelModelGrader(labelModelGrader: LabelModelGrader): T + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader): T + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun visitEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity): T + + /** A PythonGrader object that runs a python script on the input. */ + fun visitEvalGraderPython(evalGraderPython: EvalGraderPython): T + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun visitEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel): T /** - * Returns a score indicating how many valid values are contained in this object - * recursively. + * Maps an unknown variant of [TestingCriterion] to a value of type [T]. * - * Used for best match union deserialization. + * An instance of [TestingCriterion] 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. */ - @JvmSynthetic - internal fun validity(): Int = - (if (name.asKnown().isPresent) 1 else 0) + - (if (source.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("python")) 1 else 0 } + - (if (imageTag.asKnown().isPresent) 1 else 0) + - (if (passThreshold.asKnown().isPresent) 1 else 0) + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown TestingCriterion: $json") + } + } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + internal class Deserializer : BaseDeserializer(TestingCriterion::class) { - return /* spotless:off */ other is Python && name == other.name && source == other.source && type == other.type && imageTag == other.imageTag && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ - } + override fun ObjectCodec.deserialize(node: JsonNode): TestingCriterion { + val json = JsonValue.fromJsonNode(node) - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, additionalProperties) } - /* spotless:on */ + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(labelModelGrader = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(stringCheckGrader = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(evalGraderTextSimilarity = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(evalGraderPython = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(evalGraderScoreModel = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> TestingCriterion(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } - override fun hashCode(): Int = hashCode + internal class Serializer : BaseSerializer(TestingCriterion::class) { - override fun toString() = - "Python{name=$name, source=$source, type=$type, imageTag=$imageTag, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" + override fun serialize( + value: TestingCriterion, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.labelModelGrader != null -> generator.writeObject(value.labelModelGrader) + value.stringCheckGrader != null -> + generator.writeObject(value.stringCheckGrader) + value.evalGraderTextSimilarity != null -> + generator.writeObject(value.evalGraderTextSimilarity) + value.evalGraderPython != null -> generator.writeObject(value.evalGraderPython) + value.evalGraderScoreModel != null -> + generator.writeObject(value.evalGraderScoreModel) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid TestingCriterion") + } + } } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - class ScoreModel + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + class EvalGraderTextSimilarity private constructor( - private val input: JsonField>, - private val model: JsonField, + private val evaluationMetric: JsonField, + private val input: JsonField, private val name: JsonField, + private val reference: JsonField, private val type: JsonValue, private val passThreshold: JsonField, - private val range: JsonField>, - private val samplingParams: JsonValue, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("input") + @JsonProperty("evaluation_metric") @ExcludeMissing - input: JsonField> = JsonMissing.of(), - @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + evaluationMetric: JsonField = + JsonMissing.of(), + @JsonProperty("input") @ExcludeMissing input: JsonField = JsonMissing.of(), @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("reference") + @ExcludeMissing + reference: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("pass_threshold") @ExcludeMissing passThreshold: JsonField = JsonMissing.of(), - @JsonProperty("range") - @ExcludeMissing - range: JsonField> = JsonMissing.of(), - @JsonProperty("sampling_params") - @ExcludeMissing - samplingParams: JsonValue = JsonMissing.of(), - ) : this(input, model, name, type, passThreshold, range, samplingParams, mutableMapOf()) + ) : this(evaluationMetric, input, name, reference, type, passThreshold, mutableMapOf()) + + fun toTextSimilarityGrader(): TextSimilarityGrader = + TextSimilarityGrader.builder() + .evaluationMetric(evaluationMetric) + .input(input) + .name(name) + .reference(reference) + .type(type) + .build() /** - * The input text. This may include template strings. + * The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, + * `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun input(): List = input.getRequired("input") + fun evaluationMetric(): TextSimilarityGrader.EvaluationMetric = + evaluationMetric.getRequired("evaluation_metric") /** - * The model to use for the evaluation. + * The text being graded. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun model(): String = model.getRequired("model") + fun input(): String = input.getRequired("input") /** * The name of the grader. @@ -1534,11 +1797,20 @@ private constructor( fun name(): String = name.getRequired("name") /** - * The object type, which is always `score_model`. + * The text being graded against. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun reference(): String = reference.getRequired("reference") + + /** + * The type of grader. * * Expected to always return the following: * ```java - * JsonValue.from("score_model") + * JsonValue.from("text_similarity") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1549,44 +1821,46 @@ private constructor( /** * The threshold for the score. * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). */ - fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + fun passThreshold(): Double = passThreshold.getRequired("pass_threshold") /** - * The range of the score. Defaults to `[0, 1]`. + * Returns the raw JSON value of [evaluationMetric]. * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). + * Unlike [evaluationMetric], this method doesn't throw if the JSON field has an + * unexpected type. */ - fun range(): Optional> = range.getOptional("range") - - /** The sampling parameters for the model. */ - @JsonProperty("sampling_params") + @JsonProperty("evaluation_metric") @ExcludeMissing - fun _samplingParams(): JsonValue = samplingParams + fun _evaluationMetric(): JsonField = + evaluationMetric /** * Returns the raw JSON value of [input]. * * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("input") @ExcludeMissing fun _input(): JsonField> = input + @JsonProperty("input") @ExcludeMissing fun _input(): JsonField = input /** - * Returns the raw JSON value of [model]. + * Returns the raw JSON value of [name]. * - * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name /** - * Returns the raw JSON value of [name]. + * Returns the raw JSON value of [reference]. * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected + * type. */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JsonProperty("reference") + @ExcludeMissing + fun _reference(): JsonField = reference /** * Returns the raw JSON value of [passThreshold]. @@ -1598,13 +1872,6 @@ private constructor( @ExcludeMissing fun _passThreshold(): JsonField = passThreshold - /** - * Returns the raw JSON value of [range]. - * - * Unlike [range], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("range") @ExcludeMissing fun _range(): JsonField> = range - @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { additionalProperties.put(key, value) @@ -1620,79 +1887,74 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [ScoreModel]. + * Returns a mutable builder for constructing an instance of + * [EvalGraderTextSimilarity]. * * The following fields are required: * ```java + * .evaluationMetric() * .input() - * .model() * .name() + * .reference() + * .passThreshold() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [ScoreModel]. */ + /** A builder for [EvalGraderTextSimilarity]. */ class Builder internal constructor() { - private var input: JsonField>? = null - private var model: JsonField? = null + private var evaluationMetric: JsonField? = + null + private var input: JsonField? = null private var name: JsonField? = null - private var type: JsonValue = JsonValue.from("score_model") - private var passThreshold: JsonField = JsonMissing.of() - private var range: JsonField>? = null - private var samplingParams: JsonValue = JsonMissing.of() + private var reference: JsonField? = null + private var type: JsonValue = JsonValue.from("text_similarity") + private var passThreshold: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(scoreModel: ScoreModel) = apply { - input = scoreModel.input.map { it.toMutableList() } - model = scoreModel.model - name = scoreModel.name - type = scoreModel.type - passThreshold = scoreModel.passThreshold - range = scoreModel.range.map { it.toMutableList() } - samplingParams = scoreModel.samplingParams - additionalProperties = scoreModel.additionalProperties.toMutableMap() + internal fun from(evalGraderTextSimilarity: EvalGraderTextSimilarity) = apply { + evaluationMetric = evalGraderTextSimilarity.evaluationMetric + input = evalGraderTextSimilarity.input + name = evalGraderTextSimilarity.name + reference = evalGraderTextSimilarity.reference + type = evalGraderTextSimilarity.type + passThreshold = evalGraderTextSimilarity.passThreshold + additionalProperties = + evalGraderTextSimilarity.additionalProperties.toMutableMap() } - /** The input text. This may include template strings. */ - fun input(input: List) = input(JsonField.of(input)) - /** - * Sets [Builder.input] to an arbitrary JSON value. - * - * You should usually call [Builder.input] with a well-typed `List` value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. + * The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, + * `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. */ - fun input(input: JsonField>) = apply { - this.input = input.map { it.toMutableList() } - } + fun evaluationMetric(evaluationMetric: TextSimilarityGrader.EvaluationMetric) = + evaluationMetric(JsonField.of(evaluationMetric)) /** - * Adds a single [Input] to [Builder.input]. + * Sets [Builder.evaluationMetric] to an arbitrary JSON value. * - * @throws IllegalStateException if the field was previously set to a non-list. + * You should usually call [Builder.evaluationMetric] with a well-typed + * [TextSimilarityGrader.EvaluationMetric] value instead. This method is primarily + * for setting the field to an undocumented or not yet supported value. */ - fun addInput(input: Input) = apply { - this.input = - (this.input ?: JsonField.of(mutableListOf())).also { - checkKnown("input", it).add(input) - } - } + fun evaluationMetric( + evaluationMetric: JsonField + ) = apply { this.evaluationMetric = evaluationMetric } - /** The model to use for the evaluation. */ - fun model(model: String) = model(JsonField.of(model)) + /** The text being graded. */ + fun input(input: String) = input(JsonField.of(input)) /** - * Sets [Builder.model] to an arbitrary JSON value. + * Sets [Builder.input] to an arbitrary JSON value. * - * You should usually call [Builder.model] with a well-typed [String] value instead. + * You should usually call [Builder.input] with a well-typed [String] value instead. * This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun model(model: JsonField) = apply { this.model = model } + fun input(input: JsonField) = apply { this.input = input } /** The name of the grader. */ fun name(name: String) = name(JsonField.of(name)) @@ -1706,13 +1968,25 @@ private constructor( */ fun name(name: JsonField) = apply { this.name = name } + /** The text being graded against. */ + fun reference(reference: String) = reference(JsonField.of(reference)) + + /** + * Sets [Builder.reference] to an arbitrary JSON value. + * + * You should usually call [Builder.reference] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun reference(reference: JsonField) = apply { this.reference = reference } + /** * Sets the field to an arbitrary JSON value. * * It is usually unnecessary to call this method because the field defaults to the * following: * ```java - * JsonValue.from("score_model") + * JsonValue.from("text_similarity") * ``` * * This method is primarily for setting the field to an undocumented or not yet @@ -1735,37 +2009,6 @@ private constructor( this.passThreshold = passThreshold } - /** The range of the score. Defaults to `[0, 1]`. */ - fun range(range: List) = range(JsonField.of(range)) - - /** - * Sets [Builder.range] to an arbitrary JSON value. - * - * You should usually call [Builder.range] with a well-typed `List` value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun range(range: JsonField>) = apply { - this.range = range.map { it.toMutableList() } - } - - /** - * Adds a single [Double] to [Builder.range]. - * - * @throws IllegalStateException if the field was previously set to a non-list. - */ - fun addRange(range: Double) = apply { - this.range = - (this.range ?: JsonField.of(mutableListOf())).also { - checkKnown("range", it).add(range) - } - } - - /** The sampling parameters for the model. */ - fun samplingParams(samplingParams: JsonValue) = apply { - this.samplingParams = samplingParams - } - fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() putAllAdditionalProperties(additionalProperties) @@ -1789,1069 +2032,835 @@ private constructor( } /** - * Returns an immutable instance of [ScoreModel]. + * Returns an immutable instance of [EvalGraderTextSimilarity]. * * Further updates to this [Builder] will not mutate the returned instance. * * The following fields are required: * ```java + * .evaluationMetric() * .input() - * .model() * .name() + * .reference() + * .passThreshold() * ``` * * @throws IllegalStateException if any required field is unset. */ - fun build(): ScoreModel = - ScoreModel( - checkRequired("input", input).map { it.toImmutable() }, - checkRequired("model", model), + fun build(): EvalGraderTextSimilarity = + EvalGraderTextSimilarity( + checkRequired("evaluationMetric", evaluationMetric), + checkRequired("input", input), checkRequired("name", name), + checkRequired("reference", reference), type, - passThreshold, - (range ?: JsonMissing.of()).map { it.toImmutable() }, - samplingParams, + checkRequired("passThreshold", passThreshold), additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): ScoreModel = apply { + fun validate(): EvalGraderTextSimilarity = apply { if (validated) { return@apply } - input().forEach { it.validate() } - model() + evaluationMetric().validate() + input() name() + reference() _type().let { - if (it != JsonValue.from("score_model")) { - throw OpenAIInvalidDataException("'type' is invalid, received $it") - } - } - passThreshold() - range() - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (input.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + - (if (model.asKnown().isPresent) 1 else 0) + - (if (name.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("score_model")) 1 else 0 } + - (if (passThreshold.asKnown().isPresent) 1 else 0) + - (range.asKnown().getOrNull()?.size ?: 0) - - /** - * A message input to the model with a role indicating instruction following hierarchy. - * Instructions given with the `developer` or `system` role take precedence over - * instructions given with the `user` role. Messages with the `assistant` role are - * presumed to have been generated by the model in previous interactions. - */ - class Input - private constructor( - private val content: JsonField, - private val role: JsonField, - private val type: JsonField, - private val additionalProperties: MutableMap, - ) { - - @JsonCreator - private constructor( - @JsonProperty("content") - @ExcludeMissing - content: JsonField = JsonMissing.of(), - @JsonProperty("role") @ExcludeMissing role: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), - ) : this(content, role, type, mutableMapOf()) - - /** - * Text inputs to the model - can contain template strings. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun content(): Content = content.getRequired("content") - - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun role(): Role = role.getRequired("role") - - /** - * The type of the message input. Always `message`. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun type(): Optional = type.getOptional("type") - - /** - * Returns the raw JSON value of [content]. - * - * Unlike [content], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("content") - @ExcludeMissing - fun _content(): JsonField = content - - /** - * Returns the raw JSON value of [role]. - * - * Unlike [role], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("role") @ExcludeMissing fun _role(): JsonField = role - - /** - * Returns the raw JSON value of [type]. - * - * Unlike [type], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { + if (it != JsonValue.from("text_similarity")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + passThreshold() + validated = true + } - /** - * Returns a mutable builder for constructing an instance of [Input]. - * - * The following fields are required: - * ```java - * .content() - * .role() - * ``` - */ - @JvmStatic fun builder() = Builder() + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false } - /** A builder for [Input]. */ - class Builder internal constructor() { + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (evaluationMetric.asKnown().getOrNull()?.validity() ?: 0) + + (if (input.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (reference.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("text_similarity")) 1 else 0 } + + (if (passThreshold.asKnown().isPresent) 1 else 0) - private var content: JsonField? = null - private var role: JsonField? = null - private var type: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - @JvmSynthetic - internal fun from(input: Input) = apply { - content = input.content - role = input.role - type = input.type - additionalProperties = input.additionalProperties.toMutableMap() - } + return /* spotless:off */ other is EvalGraderTextSimilarity && evaluationMetric == other.evaluationMetric && input == other.input && name == other.name && reference == other.reference && type == other.type && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ + } - /** Text inputs to the model - can contain template strings. */ - fun content(content: Content) = content(JsonField.of(content)) + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(evaluationMetric, input, name, reference, type, passThreshold, additionalProperties) } + /* spotless:on */ - /** - * Sets [Builder.content] to an arbitrary JSON value. - * - * You should usually call [Builder.content] with a well-typed [Content] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun content(content: JsonField) = apply { this.content = content } + override fun hashCode(): Int = hashCode - /** Alias for calling [content] with `Content.ofTextInput(textInput)`. */ - fun content(textInput: String) = content(Content.ofTextInput(textInput)) + override fun toString() = + "EvalGraderTextSimilarity{evaluationMetric=$evaluationMetric, input=$input, name=$name, reference=$reference, type=$type, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" + } - /** - * Alias for calling [content] with - * `Content.ofResponseInputText(responseInputText)`. - */ - fun content(responseInputText: ResponseInputText) = - content(Content.ofResponseInputText(responseInputText)) + /** A PythonGrader object that runs a python script on the input. */ + class EvalGraderPython + private constructor( + private val name: JsonField, + private val source: JsonField, + private val type: JsonValue, + private val imageTag: JsonField, + private val passThreshold: JsonField, + private val additionalProperties: MutableMap, + ) { - /** Alias for calling [content] with `Content.ofOutputText(outputText)`. */ - fun content(outputText: Content.OutputText) = - content(Content.ofOutputText(outputText)) + @JsonCreator + private constructor( + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("source") + @ExcludeMissing + source: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("image_tag") + @ExcludeMissing + imageTag: JsonField = JsonMissing.of(), + @JsonProperty("pass_threshold") + @ExcludeMissing + passThreshold: JsonField = JsonMissing.of(), + ) : this(name, source, type, imageTag, passThreshold, mutableMapOf()) - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - */ - fun role(role: Role) = role(JsonField.of(role)) + fun toPythonGrader(): PythonGrader = + PythonGrader.builder() + .name(name) + .source(source) + .type(type) + .imageTag(imageTag) + .build() - /** - * Sets [Builder.role] to an arbitrary JSON value. - * - * You should usually call [Builder.role] with a well-typed [Role] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun role(role: JsonField) = apply { this.role = role } + /** + * The name of the grader. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun name(): String = name.getRequired("name") - /** The type of the message input. Always `message`. */ - fun type(type: Type) = type(JsonField.of(type)) + /** + * The source code of the python script. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun source(): String = source.getRequired("source") - /** - * Sets [Builder.type] to an arbitrary JSON value. - * - * You should usually call [Builder.type] with a well-typed [Type] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun type(type: JsonField) = apply { this.type = type } + /** + * The object type, which is always `python`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("python") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + /** + * The image tag to use for the python script. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun imageTag(): Optional = imageTag.getOptional("image_tag") - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + /** + * The threshold for the score. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") - fun putAllAdditionalProperties(additionalProperties: Map) = - apply { - this.additionalProperties.putAll(additionalProperties) - } + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name - fun removeAdditionalProperty(key: String) = apply { - additionalProperties.remove(key) - } + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** + * Returns the raw JSON value of [imageTag]. + * + * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag - /** - * Returns an immutable instance of [Input]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .content() - * .role() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): Input = - Input( - checkRequired("content", content), - checkRequired("role", role), - type, - additionalProperties.toMutableMap(), - ) - } + /** + * Returns the raw JSON value of [passThreshold]. + * + * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pass_threshold") + @ExcludeMissing + fun _passThreshold(): JsonField = passThreshold - private var validated: Boolean = false + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } - fun validate(): Input = apply { - if (validated) { - return@apply - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - content().validate() - role().validate() - type().ifPresent { it.validate() } - validated = true - } + fun toBuilder() = Builder().from(this) - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + companion object { /** - * Returns a score indicating how many valid values are contained in this object - * recursively. + * Returns a mutable builder for constructing an instance of [EvalGraderPython]. * - * Used for best match union deserialization. + * The following fields are required: + * ```java + * .name() + * .source() + * ``` */ - @JvmSynthetic - internal fun validity(): Int = - (content.asKnown().getOrNull()?.validity() ?: 0) + - (role.asKnown().getOrNull()?.validity() ?: 0) + - (type.asKnown().getOrNull()?.validity() ?: 0) - - /** Text inputs to the model - can contain template strings. */ - @JsonDeserialize(using = Content.Deserializer::class) - @JsonSerialize(using = Content.Serializer::class) - class Content - private constructor( - private val textInput: String? = null, - private val responseInputText: ResponseInputText? = null, - private val outputText: OutputText? = null, - private val _json: JsonValue? = null, - ) { - - /** A text input to the model. */ - fun textInput(): Optional = Optional.ofNullable(textInput) - - /** A text input to the model. */ - fun responseInputText(): Optional = - Optional.ofNullable(responseInputText) - - /** A text output from the model. */ - fun outputText(): Optional = Optional.ofNullable(outputText) - - fun isTextInput(): Boolean = textInput != null - - fun isResponseInputText(): Boolean = responseInputText != null - - fun isOutputText(): Boolean = outputText != null - - /** A text input to the model. */ - fun asTextInput(): String = textInput.getOrThrow("textInput") - - /** A text input to the model. */ - fun asResponseInputText(): ResponseInputText = - responseInputText.getOrThrow("responseInputText") + @JvmStatic fun builder() = Builder() + } - /** A text output from the model. */ - fun asOutputText(): OutputText = outputText.getOrThrow("outputText") + /** A builder for [EvalGraderPython]. */ + class Builder internal constructor() { - fun _json(): Optional = Optional.ofNullable(_json) + private var name: JsonField? = null + private var source: JsonField? = null + private var type: JsonValue = JsonValue.from("python") + private var imageTag: JsonField = JsonMissing.of() + private var passThreshold: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() - fun accept(visitor: Visitor): T = - when { - textInput != null -> visitor.visitTextInput(textInput) - responseInputText != null -> - visitor.visitResponseInputText(responseInputText) - outputText != null -> visitor.visitOutputText(outputText) - else -> visitor.unknown(_json) - } + @JvmSynthetic + internal fun from(evalGraderPython: EvalGraderPython) = apply { + name = evalGraderPython.name + source = evalGraderPython.source + type = evalGraderPython.type + imageTag = evalGraderPython.imageTag + passThreshold = evalGraderPython.passThreshold + additionalProperties = evalGraderPython.additionalProperties.toMutableMap() + } - private var validated: Boolean = false + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) - fun validate(): Content = apply { - if (validated) { - return@apply - } + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun name(name: JsonField) = apply { this.name = name } - accept( - object : Visitor { - override fun visitTextInput(textInput: String) {} + /** The source code of the python script. */ + fun source(source: String) = source(JsonField.of(source)) - override fun visitResponseInputText( - responseInputText: ResponseInputText - ) { - responseInputText.validate() - } + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun source(source: JsonField) = apply { this.source = source } - override fun visitOutputText(outputText: OutputText) { - outputText.validate() - } - } - ) - validated = true - } + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("python") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + /** The image tag to use for the python script. */ + fun imageTag(imageTag: String) = imageTag(JsonField.of(imageTag)) - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitTextInput(textInput: String) = 1 + /** + * Sets [Builder.imageTag] to an arbitrary JSON value. + * + * You should usually call [Builder.imageTag] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun imageTag(imageTag: JsonField) = apply { this.imageTag = imageTag } - override fun visitResponseInputText( - responseInputText: ResponseInputText - ) = responseInputText.validity() + /** The threshold for the score. */ + fun passThreshold(passThreshold: Double) = + passThreshold(JsonField.of(passThreshold)) - override fun visitOutputText(outputText: OutputText) = - outputText.validity() + /** + * Sets [Builder.passThreshold] to an arbitrary JSON value. + * + * You should usually call [Builder.passThreshold] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun passThreshold(passThreshold: JsonField) = apply { + this.passThreshold = passThreshold + } - override fun unknown(json: JsonValue?) = 0 - } - ) + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - return /* spotless:off */ other is Content && textInput == other.textInput && responseInputText == other.responseInputText && outputText == other.outputText /* spotless:on */ + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(textInput, responseInputText, outputText) /* spotless:on */ - - override fun toString(): String = - when { - textInput != null -> "Content{textInput=$textInput}" - responseInputText != null -> - "Content{responseInputText=$responseInputText}" - outputText != null -> "Content{outputText=$outputText}" - _json != null -> "Content{_unknown=$_json}" - else -> throw IllegalStateException("Invalid Content") - } - - companion object { + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - /** A text input to the model. */ - @JvmStatic - fun ofTextInput(textInput: String) = Content(textInput = textInput) + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - /** A text input to the model. */ - @JvmStatic - fun ofResponseInputText(responseInputText: ResponseInputText) = - Content(responseInputText = responseInputText) + /** + * Returns an immutable instance of [EvalGraderPython]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .name() + * .source() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): EvalGraderPython = + EvalGraderPython( + checkRequired("name", name), + checkRequired("source", source), + type, + imageTag, + passThreshold, + additionalProperties.toMutableMap(), + ) + } - /** A text output from the model. */ - @JvmStatic - fun ofOutputText(outputText: OutputText) = Content(outputText = outputText) - } + private var validated: Boolean = false - /** - * An interface that defines how to map each variant of [Content] to a value of - * type [T]. - */ - interface Visitor { - - /** A text input to the model. */ - fun visitTextInput(textInput: String): T - - /** A text input to the model. */ - fun visitResponseInputText(responseInputText: ResponseInputText): T - - /** A text output from the model. */ - fun visitOutputText(outputText: OutputText): T - - /** - * Maps an unknown variant of [Content] to a value of type [T]. - * - * An instance of [Content] 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 Content: $json") - } - } + fun validate(): EvalGraderPython = apply { + if (validated) { + return@apply + } - internal class Deserializer : BaseDeserializer(Content::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): Content { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { Content(responseInputText = it, _json = json) }, - tryDeserialize(node, jacksonTypeRef())?.let { - Content(outputText = it, _json = json) - }, - tryDeserialize(node, jacksonTypeRef())?.let { - Content(textInput = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from array). - 0 -> Content(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } + name() + source() + _type().let { + if (it != JsonValue.from("python")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") } + } + imageTag() + passThreshold() + validated = true + } - internal class Serializer : BaseSerializer(Content::class) { - - override fun serialize( - value: Content, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.textInput != null -> generator.writeObject(value.textInput) - value.responseInputText != null -> - generator.writeObject(value.responseInputText) - value.outputText != null -> generator.writeObject(value.outputText) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid Content") - } - } - } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - /** A text output from the model. */ - class OutputText - private constructor( - private val text: JsonField, - private val type: JsonValue, - private val additionalProperties: MutableMap, - ) { + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (name.asKnown().isPresent) 1 else 0) + + (if (source.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("python")) 1 else 0 } + + (if (imageTag.asKnown().isPresent) 1 else 0) + + (if (passThreshold.asKnown().isPresent) 1 else 0) - @JsonCreator - private constructor( - @JsonProperty("text") - @ExcludeMissing - text: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - ) : this(text, type, mutableMapOf()) - - /** - * The text output from the model. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected - * type or is unexpectedly missing or null (e.g. if the server responded - * with an unexpected value). - */ - fun text(): String = text.getRequired("text") - - /** - * The type of the output text. Always `output_text`. - * - * Expected to always return the following: - * ```java - * JsonValue.from("output_text") - * ``` - * - * However, this method can be useful for debugging and logging (e.g. if the - * server responded with an unexpected value). - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - - /** - * Returns the raw JSON value of [text]. - * - * Unlike [text], this method doesn't throw if the JSON field has an - * unexpected type. - */ - @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { - - /** - * Returns a mutable builder for constructing an instance of - * [OutputText]. - * - * The following fields are required: - * ```java - * .text() - * ``` - */ - @JvmStatic fun builder() = Builder() - } + return /* spotless:off */ other is EvalGraderPython && name == other.name && source == other.source && type == other.type && imageTag == other.imageTag && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ + } - /** A builder for [OutputText]. */ - class Builder internal constructor() { - - private var text: JsonField? = null - private var type: JsonValue = JsonValue.from("output_text") - private var additionalProperties: MutableMap = - mutableMapOf() - - @JvmSynthetic - internal fun from(outputText: OutputText) = apply { - text = outputText.text - type = outputText.type - additionalProperties = - outputText.additionalProperties.toMutableMap() - } - - /** The text output from the model. */ - fun text(text: String) = text(JsonField.of(text)) - - /** - * Sets [Builder.text] to an arbitrary JSON value. - * - * You should usually call [Builder.text] with a well-typed [String] - * value instead. This method is primarily for setting the field to an - * undocumented or not yet supported value. - */ - fun text(text: JsonField) = apply { this.text = text } - - /** - * Sets the field to an arbitrary JSON value. - * - * It is usually unnecessary to call this method because the field - * defaults to the following: - * ```java - * JsonValue.from("output_text") - * ``` - * - * This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun type(type: JsonValue) = apply { this.type = type } - - 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) - } - - /** - * Returns an immutable instance of [OutputText]. - * - * Further updates to this [Builder] will not mutate the returned - * instance. - * - * The following fields are required: - * ```java - * .text() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): OutputText = - OutputText( - checkRequired("text", text), - type, - additionalProperties.toMutableMap(), - ) - } + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, additionalProperties) } + /* spotless:on */ - private var validated: Boolean = false - - fun validate(): OutputText = apply { - if (validated) { - return@apply - } - - text() - _type().let { - if (it != JsonValue.from("output_text")) { - throw OpenAIInvalidDataException( - "'type' is invalid, received $it" - ) - } - } - validated = true - } + override fun hashCode(): Int = hashCode - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this - * object recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (if (text.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("output_text")) 1 else 0 } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is OutputText && text == other.text && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ - } + override fun toString() = + "EvalGraderPython{name=$name, source=$source, type=$type, imageTag=$imageTag, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" + } - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(text, type, additionalProperties) } - /* spotless:on */ + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + class EvalGraderScoreModel + private constructor( + private val input: JsonField>, + private val model: JsonField, + private val name: JsonField, + private val type: JsonValue, + private val range: JsonField>, + private val samplingParams: JsonValue, + private val passThreshold: JsonField, + private val additionalProperties: MutableMap, + ) { - override fun hashCode(): Int = hashCode + @JsonCreator + private constructor( + @JsonProperty("input") + @ExcludeMissing + input: JsonField> = JsonMissing.of(), + @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("range") + @ExcludeMissing + range: JsonField> = JsonMissing.of(), + @JsonProperty("sampling_params") + @ExcludeMissing + samplingParams: JsonValue = JsonMissing.of(), + @JsonProperty("pass_threshold") + @ExcludeMissing + passThreshold: JsonField = JsonMissing.of(), + ) : this(input, model, name, type, range, samplingParams, passThreshold, mutableMapOf()) + + fun toScoreModelGrader(): ScoreModelGrader = + ScoreModelGrader.builder() + .input(input) + .model(model) + .name(name) + .type(type) + .range(range) + .samplingParams(samplingParams) + .build() - override fun toString() = - "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" - } - } + /** + * The input text. This may include template strings. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun input(): List = input.getRequired("input") - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - */ - class Role @JsonCreator private constructor(private val value: JsonField) : - Enum { + /** + * The model to use for the evaluation. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun model(): String = model.getRequired("model") - /** - * 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 + /** + * The name of the grader. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun name(): String = name.getRequired("name") - companion object { + /** + * The object type, which is always `score_model`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("score_model") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - @JvmField val USER = of("user") + /** + * The range of the score. Defaults to `[0, 1]`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun range(): Optional> = range.getOptional("range") - @JvmField val ASSISTANT = of("assistant") + /** The sampling parameters for the model. */ + @JsonProperty("sampling_params") + @ExcludeMissing + fun _samplingParams(): JsonValue = samplingParams - @JvmField val SYSTEM = of("system") + /** + * The threshold for the score. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") - @JvmField val DEVELOPER = of("developer") + /** + * Returns the raw JSON value of [input]. + * + * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("input") + @ExcludeMissing + fun _input(): JsonField> = input - @JvmStatic fun of(value: String) = Role(JsonField.of(value)) - } + /** + * Returns the raw JSON value of [model]. + * + * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model - /** An enum containing [Role]'s known values. */ - enum class Known { - USER, - ASSISTANT, - SYSTEM, - DEVELOPER, - } + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name - /** - * An enum containing [Role]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Role] 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 { - USER, - ASSISTANT, - SYSTEM, - DEVELOPER, - /** - * An enum member indicating that [Role] was instantiated with an unknown - * value. - */ - _UNKNOWN, - } + /** + * Returns the raw JSON value of [range]. + * + * Unlike [range], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("range") @ExcludeMissing fun _range(): JsonField> = range - /** - * 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) { - USER -> Value.USER - ASSISTANT -> Value.ASSISTANT - SYSTEM -> Value.SYSTEM - DEVELOPER -> Value.DEVELOPER - else -> Value._UNKNOWN - } + /** + * Returns the raw JSON value of [passThreshold]. + * + * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pass_threshold") + @ExcludeMissing + fun _passThreshold(): JsonField = passThreshold - /** - * 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) { - USER -> Known.USER - ASSISTANT -> Known.ASSISTANT - SYSTEM -> Known.SYSTEM - DEVELOPER -> Known.DEVELOPER - else -> throw OpenAIInvalidDataException("Unknown Role: $value") - } + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, 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") - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - private var validated: Boolean = false + fun toBuilder() = Builder().from(this) - fun validate(): Role = apply { - if (validated) { - return@apply - } + companion object { - known() - validated = true - } + /** + * Returns a mutable builder for constructing an instance of [EvalGraderScoreModel]. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + */ + @JvmStatic fun builder() = Builder() + } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + /** A builder for [EvalGraderScoreModel]. */ + class Builder internal constructor() { - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + private var input: JsonField>? = null + private var model: JsonField? = null + private var name: JsonField? = null + private var type: JsonValue = JsonValue.from("score_model") + private var range: JsonField>? = null + private var samplingParams: JsonValue = JsonMissing.of() + private var passThreshold: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + @JvmSynthetic + internal fun from(evalGraderScoreModel: EvalGraderScoreModel) = apply { + input = evalGraderScoreModel.input.map { it.toMutableList() } + model = evalGraderScoreModel.model + name = evalGraderScoreModel.name + type = evalGraderScoreModel.type + range = evalGraderScoreModel.range.map { it.toMutableList() } + samplingParams = evalGraderScoreModel.samplingParams + passThreshold = evalGraderScoreModel.passThreshold + additionalProperties = evalGraderScoreModel.additionalProperties.toMutableMap() + } - return /* spotless:off */ other is Role && value == other.value /* spotless:on */ - } + /** The input text. This may include template strings. */ + fun input(input: List) = input(JsonField.of(input)) - override fun hashCode() = value.hashCode() + /** + * Sets [Builder.input] to an arbitrary JSON value. + * + * You should usually call [Builder.input] with a well-typed + * `List` value instead. This method is primarily for + * setting the field to an undocumented or not yet supported value. + */ + fun input(input: JsonField>) = apply { + this.input = input.map { it.toMutableList() } + } - override fun toString() = value.toString() + /** + * Adds a single [ScoreModelGrader.Input] to [Builder.input]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addInput(input: ScoreModelGrader.Input) = apply { + this.input = + (this.input ?: JsonField.of(mutableListOf())).also { + checkKnown("input", it).add(input) + } } - /** The type of the message input. Always `message`. */ - class Type @JsonCreator private constructor(private val value: JsonField) : - Enum { + /** The model to use for the evaluation. */ + fun model(model: String) = model(JsonField.of(model)) - /** - * 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 + /** + * Sets [Builder.model] to an arbitrary JSON value. + * + * You should usually call [Builder.model] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun model(model: JsonField) = apply { this.model = model } - companion object { + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) - @JvmField val MESSAGE = of("message") + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun name(name: JsonField) = apply { this.name = name } - @JvmStatic fun of(value: String) = Type(JsonField.of(value)) - } + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("score_model") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - /** An enum containing [Type]'s known values. */ - enum class Known { - MESSAGE - } + /** The range of the score. Defaults to `[0, 1]`. */ + fun range(range: List) = range(JsonField.of(range)) - /** - * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Type] 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 { - MESSAGE, - /** - * An enum member indicating that [Type] was instantiated with an unknown - * value. - */ - _UNKNOWN, - } + /** + * Sets [Builder.range] to an arbitrary JSON value. + * + * You should usually call [Builder.range] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun range(range: JsonField>) = apply { + this.range = range.map { it.toMutableList() } + } - /** - * 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) { - MESSAGE -> Value.MESSAGE - else -> Value._UNKNOWN + /** + * Adds a single [Double] to [Builder.range]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addRange(range: Double) = apply { + this.range = + (this.range ?: JsonField.of(mutableListOf())).also { + checkKnown("range", it).add(range) } + } - /** - * 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) { - MESSAGE -> Known.MESSAGE - else -> throw OpenAIInvalidDataException("Unknown Type: $value") - } + /** The sampling parameters for the model. */ + fun samplingParams(samplingParams: JsonValue) = apply { + this.samplingParams = samplingParams + } - /** - * 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") - } + /** The threshold for the score. */ + fun passThreshold(passThreshold: Double) = + passThreshold(JsonField.of(passThreshold)) + + /** + * Sets [Builder.passThreshold] to an arbitrary JSON value. + * + * You should usually call [Builder.passThreshold] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun passThreshold(passThreshold: JsonField) = apply { + this.passThreshold = passThreshold + } - private var validated: Boolean = false + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - fun validate(): Type = apply { - if (validated) { - return@apply - } + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - known() - validated = true + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - return /* spotless:off */ other is Type && value == other.value /* spotless:on */ - } + /** + * Returns an immutable instance of [EvalGraderScoreModel]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): EvalGraderScoreModel = + EvalGraderScoreModel( + checkRequired("input", input).map { it.toImmutable() }, + checkRequired("model", model), + checkRequired("name", name), + type, + (range ?: JsonMissing.of()).map { it.toImmutable() }, + samplingParams, + passThreshold, + additionalProperties.toMutableMap(), + ) + } - override fun hashCode() = value.hashCode() + private var validated: Boolean = false - override fun toString() = value.toString() + fun validate(): EvalGraderScoreModel = apply { + if (validated) { + return@apply } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + input().forEach { it.validate() } + model() + name() + _type().let { + if (it != JsonValue.from("score_model")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") } - - return /* spotless:off */ other is Input && content == other.content && role == other.role && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ } + range() + passThreshold() + validated = true + } - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(content, role, type, additionalProperties) } - /* spotless:on */ - - override fun hashCode(): Int = hashCode + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - override fun toString() = - "Input{content=$content, role=$role, type=$type, additionalProperties=$additionalProperties}" - } + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (input.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (model.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("score_model")) 1 else 0 } + + (range.asKnown().getOrNull()?.size ?: 0) + + (if (passThreshold.asKnown().isPresent) 1 else 0) override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is ScoreModel && input == other.input && model == other.model && name == other.name && type == other.type && passThreshold == other.passThreshold && range == other.range && samplingParams == other.samplingParams && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is EvalGraderScoreModel && input == other.input && model == other.model && name == other.name && type == other.type && range == other.range && samplingParams == other.samplingParams && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(input, model, name, type, passThreshold, range, samplingParams, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(input, model, name, type, range, samplingParams, passThreshold, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "ScoreModel{input=$input, model=$model, name=$name, type=$type, passThreshold=$passThreshold, range=$range, samplingParams=$samplingParams, additionalProperties=$additionalProperties}" + "EvalGraderScoreModel{input=$input, model=$model, name=$name, type=$type, range=$range, samplingParams=$samplingParams, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalStoredCompletionsDataSourceConfig.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalStoredCompletionsDataSourceConfig.kt index d7d5d289..7123c36a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalStoredCompletionsDataSourceConfig.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalStoredCompletionsDataSourceConfig.kt @@ -18,12 +18,8 @@ import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your stored - * completions query. This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. - * The schema returned by this data source config is used to defined what variables are available in - * your evals. `item` and `sample` are both defined when using this data source config. - */ +/** Deprecated in favor of LogsDataSourceConfig. */ +@Deprecated("deprecated") class EvalStoredCompletionsDataSourceConfig private constructor( private val schema: JsonField, diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalUpdateParams.kt index c5ea3694..d3556b08 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalUpdateParams.kt @@ -11,7 +11,6 @@ import com.openai.core.JsonField import com.openai.core.JsonMissing import com.openai.core.JsonValue 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 @@ -24,13 +23,13 @@ import kotlin.jvm.optionals.getOrNull /** Update certain properties of an evaluation. */ class EvalUpdateParams private constructor( - private val evalId: String, + private val evalId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun evalId(): String = evalId + fun evalId(): Optional = Optional.ofNullable(evalId) /** * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing @@ -77,14 +76,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [EvalUpdateParams]. - * - * The following fields are required: - * ```java - * .evalId() - * ``` - */ + @JvmStatic fun none(): EvalUpdateParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [EvalUpdateParams]. */ @JvmStatic fun builder() = Builder() } @@ -104,7 +98,10 @@ private constructor( additionalQueryParams = evalUpdateParams.additionalQueryParams.toBuilder() } - fun evalId(evalId: String) = apply { this.evalId = evalId } + fun evalId(evalId: String?) = apply { this.evalId = evalId } + + /** Alias for calling [Builder.evalId] with `evalId.orElse(null)`. */ + fun evalId(evalId: Optional) = evalId(evalId.getOrNull()) /** * Sets the entire request body. @@ -270,17 +267,10 @@ private constructor( * Returns an immutable instance of [EvalUpdateParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .evalId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): EvalUpdateParams = EvalUpdateParams( - checkRequired("evalId", evalId), + evalId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -291,7 +281,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> evalId + 0 -> evalId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalUpdateResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalUpdateResponse.kt index db7ad9a1..82ec8546 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalUpdateResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalUpdateResponse.kt @@ -15,7 +15,6 @@ 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.Enum import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing @@ -26,7 +25,11 @@ import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException -import com.openai.models.responses.ResponseInputText +import com.openai.models.graders.gradermodels.LabelModelGrader +import com.openai.models.graders.gradermodels.PythonGrader +import com.openai.models.graders.gradermodels.ScoreModelGrader +import com.openai.models.graders.gradermodels.StringCheckGrader +import com.openai.models.graders.gradermodels.TextSimilarityGrader import java.util.Collections import java.util.Objects import java.util.Optional @@ -37,7 +40,7 @@ import kotlin.jvm.optionals.getOrNull * done for your LLM integration. Like: * - Improve the quality of my chatbot * - See how well my chatbot handles customer support - * - Check if o3-mini is better at my usecase than gpt-4o + * - Check if o4-mini is better at my usecase than gpt-4o */ class EvalUpdateResponse private constructor( @@ -294,10 +297,26 @@ private constructor( fun customDataSourceConfig(schema: EvalCustomDataSourceConfig.Schema) = dataSourceConfig(EvalCustomDataSourceConfig.builder().schema(schema).build()) + /** Alias for calling [dataSourceConfig] with `DataSourceConfig.ofLogs(logs)`. */ + fun dataSourceConfig(logs: DataSourceConfig.Logs) = + dataSourceConfig(DataSourceConfig.ofLogs(logs)) + + /** + * Alias for calling [dataSourceConfig] with the following: + * ```java + * DataSourceConfig.Logs.builder() + * .schema(schema) + * .build() + * ``` + */ + fun logsDataSourceConfig(schema: DataSourceConfig.Logs.Schema) = + dataSourceConfig(DataSourceConfig.Logs.builder().schema(schema).build()) + /** * Alias for calling [dataSourceConfig] with * `DataSourceConfig.ofStoredCompletions(storedCompletions)`. */ + @Deprecated("deprecated") fun dataSourceConfig(storedCompletions: EvalStoredCompletionsDataSourceConfig) = dataSourceConfig(DataSourceConfig.ofStoredCompletions(storedCompletions)) @@ -309,6 +328,7 @@ private constructor( * .build() * ``` */ + @Deprecated("deprecated") fun storedCompletionsDataSourceConfig( schema: EvalStoredCompletionsDataSourceConfig.Schema ) = dataSourceConfig(EvalStoredCompletionsDataSourceConfig.builder().schema(schema).build()) @@ -388,34 +408,43 @@ private constructor( } /** - * Alias for calling [addTestingCriterion] with `TestingCriterion.ofLabelModel(labelModel)`. + * Alias for calling [addTestingCriterion] with + * `TestingCriterion.ofLabelModelGrader(labelModelGrader)`. */ - fun addTestingCriterion(labelModel: EvalLabelModelGrader) = - addTestingCriterion(TestingCriterion.ofLabelModel(labelModel)) + fun addTestingCriterion(labelModelGrader: LabelModelGrader) = + addTestingCriterion(TestingCriterion.ofLabelModelGrader(labelModelGrader)) /** * Alias for calling [addTestingCriterion] with - * `TestingCriterion.ofStringCheck(stringCheck)`. + * `TestingCriterion.ofStringCheckGrader(stringCheckGrader)`. */ - fun addTestingCriterion(stringCheck: EvalStringCheckGrader) = - addTestingCriterion(TestingCriterion.ofStringCheck(stringCheck)) + fun addTestingCriterion(stringCheckGrader: StringCheckGrader) = + addTestingCriterion(TestingCriterion.ofStringCheckGrader(stringCheckGrader)) /** * Alias for calling [addTestingCriterion] with - * `TestingCriterion.ofTextSimilarity(textSimilarity)`. + * `TestingCriterion.ofEvalGraderTextSimilarity(evalGraderTextSimilarity)`. */ - fun addTestingCriterion(textSimilarity: EvalTextSimilarityGrader) = - addTestingCriterion(TestingCriterion.ofTextSimilarity(textSimilarity)) + fun addTestingCriterion( + evalGraderTextSimilarity: TestingCriterion.EvalGraderTextSimilarity + ) = + addTestingCriterion( + TestingCriterion.ofEvalGraderTextSimilarity(evalGraderTextSimilarity) + ) - /** Alias for calling [addTestingCriterion] with `TestingCriterion.ofPython(python)`. */ - fun addTestingCriterion(python: TestingCriterion.Python) = - addTestingCriterion(TestingCriterion.ofPython(python)) + /** + * Alias for calling [addTestingCriterion] with + * `TestingCriterion.ofEvalGraderPython(evalGraderPython)`. + */ + fun addTestingCriterion(evalGraderPython: TestingCriterion.EvalGraderPython) = + addTestingCriterion(TestingCriterion.ofEvalGraderPython(evalGraderPython)) /** - * Alias for calling [addTestingCriterion] with `TestingCriterion.ofScoreModel(scoreModel)`. + * Alias for calling [addTestingCriterion] with + * `TestingCriterion.ofEvalGraderScoreModel(evalGraderScoreModel)`. */ - fun addTestingCriterion(scoreModel: TestingCriterion.ScoreModel) = - addTestingCriterion(TestingCriterion.ofScoreModel(scoreModel)) + fun addTestingCriterion(evalGraderScoreModel: TestingCriterion.EvalGraderScoreModel) = + addTestingCriterion(TestingCriterion.ofEvalGraderScoreModel(evalGraderScoreModel)) fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -516,6 +545,7 @@ private constructor( class DataSourceConfig private constructor( private val custom: EvalCustomDataSourceConfig? = null, + private val logs: Logs? = null, private val storedCompletions: EvalStoredCompletionsDataSourceConfig? = null, private val _json: JsonValue? = null, ) { @@ -529,18 +559,23 @@ private constructor( fun custom(): Optional = Optional.ofNullable(custom) /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your stored - * completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both defined - * when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This is + * usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema returned + * by this data source config is used to defined what variables are available in your evals. + * `item` and `sample` are both defined when using this data source config. */ + fun logs(): Optional = Optional.ofNullable(logs) + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") fun storedCompletions(): Optional = Optional.ofNullable(storedCompletions) fun isCustom(): Boolean = custom != null - fun isStoredCompletions(): Boolean = storedCompletions != null + fun isLogs(): Boolean = logs != null + + @Deprecated("deprecated") fun isStoredCompletions(): Boolean = storedCompletions != null /** * A CustomDataSourceConfig which specifies the schema of your `item` and optionally @@ -551,12 +586,15 @@ private constructor( fun asCustom(): EvalCustomDataSourceConfig = custom.getOrThrow("custom") /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your stored - * completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both defined - * when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This is + * usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema returned + * by this data source config is used to defined what variables are available in your evals. + * `item` and `sample` are both defined when using this data source config. */ + fun asLogs(): Logs = logs.getOrThrow("logs") + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") fun asStoredCompletions(): EvalStoredCompletionsDataSourceConfig = storedCompletions.getOrThrow("storedCompletions") @@ -565,6 +603,7 @@ private constructor( fun accept(visitor: Visitor): T = when { custom != null -> visitor.visitCustom(custom) + logs != null -> visitor.visitLogs(logs) storedCompletions != null -> visitor.visitStoredCompletions(storedCompletions) else -> visitor.unknown(_json) } @@ -582,6 +621,10 @@ private constructor( custom.validate() } + override fun visitLogs(logs: Logs) { + logs.validate() + } + override fun visitStoredCompletions( storedCompletions: EvalStoredCompletionsDataSourceConfig ) { @@ -612,6 +655,8 @@ private constructor( object : Visitor { override fun visitCustom(custom: EvalCustomDataSourceConfig) = custom.validity() + override fun visitLogs(logs: Logs) = logs.validity() + override fun visitStoredCompletions( storedCompletions: EvalStoredCompletionsDataSourceConfig ) = storedCompletions.validity() @@ -625,14 +670,15 @@ private constructor( return true } - return /* spotless:off */ other is DataSourceConfig && custom == other.custom && storedCompletions == other.storedCompletions /* spotless:on */ + return /* spotless:off */ other is DataSourceConfig && custom == other.custom && logs == other.logs && storedCompletions == other.storedCompletions /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(custom, storedCompletions) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(custom, logs, storedCompletions) /* spotless:on */ override fun toString(): String = when { custom != null -> "DataSourceConfig{custom=$custom}" + logs != null -> "DataSourceConfig{logs=$logs}" storedCompletions != null -> "DataSourceConfig{storedCompletions=$storedCompletions}" _json != null -> "DataSourceConfig{_unknown=$_json}" @@ -651,12 +697,16 @@ private constructor( fun ofCustom(custom: EvalCustomDataSourceConfig) = DataSourceConfig(custom = custom) /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your - * stored completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both - * defined when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This + * is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema + * returned by this data source config is used to defined what variables are available + * in your evals. `item` and `sample` are both defined when using this data source + * config. */ + @JvmStatic fun ofLogs(logs: Logs) = DataSourceConfig(logs = logs) + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") @JvmStatic fun ofStoredCompletions(storedCompletions: EvalStoredCompletionsDataSourceConfig) = DataSourceConfig(storedCompletions = storedCompletions) @@ -677,12 +727,16 @@ private constructor( fun visitCustom(custom: EvalCustomDataSourceConfig): T /** - * A StoredCompletionsDataSourceConfig which specifies the metadata property of your - * stored completions query. This is usually metadata like `usecase=chatbot` or - * `prompt-version=v2`, etc. The schema returned by this data source config is used to - * defined what variables are available in your evals. `item` and `sample` are both - * defined when using this data source config. + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This + * is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema + * returned by this data source config is used to defined what variables are available + * in your evals. `item` and `sample` are both defined when using this data source + * config. */ + fun visitLogs(logs: Logs): T + + /** Deprecated in favor of LogsDataSourceConfig. */ + @Deprecated("deprecated") fun visitStoredCompletions(storedCompletions: EvalStoredCompletionsDataSourceConfig): T /** @@ -712,6 +766,11 @@ private constructor( ?.let { DataSourceConfig(custom = it, _json = json) } ?: DataSourceConfig(_json = json) } + "logs" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DataSourceConfig(logs = it, _json = json) + } ?: DataSourceConfig(_json = json) + } "stored_completions" -> { return tryDeserialize( node, @@ -735,6 +794,7 @@ private constructor( ) { when { value.custom != null -> generator.writeObject(value.custom) + value.logs != null -> generator.writeObject(value.logs) value.storedCompletions != null -> generator.writeObject(value.storedCompletions) value._json != null -> generator.writeObject(value._json) @@ -742,787 +802,990 @@ private constructor( } } } - } - - /** - * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing - * additional information about the object in a structured format, and querying for objects via - * API or the dashboard. - * - * Keys are strings with a maximum length of 64 characters. Values are strings with a maximum - * length of 512 characters. - */ - class Metadata - @JsonCreator - private constructor( - @com.fasterxml.jackson.annotation.JsonValue - private val additionalProperties: Map - ) { - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = additionalProperties - - fun toBuilder() = Builder().from(this) - - companion object { - - /** Returns a mutable builder for constructing an instance of [Metadata]. */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [Metadata]. */ - class Builder internal constructor() { - - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(metadata: Metadata) = apply { - additionalProperties = metadata.additionalProperties.toMutableMap() - } - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + /** + * A LogsDataSourceConfig which specifies the metadata property of your logs query. This is + * usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. The schema returned + * by this data source config is used to defined what variables are available in your evals. + * `item` and `sample` are both defined when using this data source config. + */ + class Logs + private constructor( + private val schema: JsonField, + private val type: JsonValue, + private val metadata: JsonField, + private val additionalProperties: MutableMap, + ) { - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + @JsonCreator + private constructor( + @JsonProperty("schema") + @ExcludeMissing + schema: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("metadata") + @ExcludeMissing + metadata: JsonField = JsonMissing.of(), + ) : this(schema, type, metadata, mutableMapOf()) - fun putAllAdditionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.putAll(additionalProperties) - } + /** + * The json schema for the run data source items. Learn how to build JSON schemas + * [here](https://json-schema.org/). + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun schema(): Schema = schema.getRequired("schema") - fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + /** + * The type of data source. Always `logs`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("logs") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** + * Set of 16 key-value pairs that can be attached to an object. This can be useful for + * storing additional information about the object in a structured format, and querying + * for objects via API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with a + * maximum length of 512 characters. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun metadata(): Optional = metadata.getOptional("metadata") /** - * Returns an immutable instance of [Metadata]. + * Returns the raw JSON value of [schema]. * - * Further updates to this [Builder] will not mutate the returned instance. + * Unlike [schema], this method doesn't throw if the JSON field has an unexpected type. */ - fun build(): Metadata = Metadata(additionalProperties.toImmutable()) - } + @JsonProperty("schema") @ExcludeMissing fun _schema(): JsonField = schema - private var validated: Boolean = false + /** + * Returns the raw JSON value of [metadata]. + * + * Unlike [metadata], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("metadata") + @ExcludeMissing + fun _metadata(): JsonField = metadata - fun validate(): Metadata = apply { - if (validated) { - return@apply + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) } - validated = true - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + fun toBuilder() = Builder().from(this) - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + companion object { - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + /** + * Returns a mutable builder for constructing an instance of [Logs]. + * + * The following fields are required: + * ```java + * .schema() + * ``` + */ + @JvmStatic fun builder() = Builder() } - return /* spotless:off */ other is Metadata && additionalProperties == other.additionalProperties /* spotless:on */ - } + /** A builder for [Logs]. */ + class Builder internal constructor() { - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(additionalProperties) } - /* spotless:on */ + private var schema: JsonField? = null + private var type: JsonValue = JsonValue.from("logs") + private var metadata: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() - override fun hashCode(): Int = hashCode + @JvmSynthetic + internal fun from(logs: Logs) = apply { + schema = logs.schema + type = logs.type + metadata = logs.metadata + additionalProperties = logs.additionalProperties.toMutableMap() + } - override fun toString() = "Metadata{additionalProperties=$additionalProperties}" - } + /** + * The json schema for the run data source items. Learn how to build JSON schemas + * [here](https://json-schema.org/). + */ + fun schema(schema: Schema) = schema(JsonField.of(schema)) - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the evaluation. - */ - @JsonDeserialize(using = TestingCriterion.Deserializer::class) - @JsonSerialize(using = TestingCriterion.Serializer::class) - class TestingCriterion - private constructor( - private val labelModel: EvalLabelModelGrader? = null, - private val stringCheck: EvalStringCheckGrader? = null, - private val textSimilarity: EvalTextSimilarityGrader? = null, - private val python: Python? = null, - private val scoreModel: ScoreModel? = null, - private val _json: JsonValue? = null, - ) { + /** + * Sets [Builder.schema] to an arbitrary JSON value. + * + * You should usually call [Builder.schema] with a well-typed [Schema] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun schema(schema: JsonField) = apply { this.schema = schema } - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - fun labelModel(): Optional = Optional.ofNullable(labelModel) + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("logs") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - /** - * A StringCheckGrader object that performs a string comparison between input and reference - * using a specified operation. - */ - fun stringCheck(): Optional = Optional.ofNullable(stringCheck) + /** + * Set of 16 key-value pairs that can be attached to an object. This can be useful + * for storing additional information about the object in a structured format, and + * querying for objects via API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with + * a maximum length of 512 characters. + */ + fun metadata(metadata: Metadata?) = metadata(JsonField.ofNullable(metadata)) - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun textSimilarity(): Optional = - Optional.ofNullable(textSimilarity) + /** Alias for calling [Builder.metadata] with `metadata.orElse(null)`. */ + fun metadata(metadata: Optional) = metadata(metadata.getOrNull()) - /** A PythonGrader object that runs a python script on the input. */ - fun python(): Optional = Optional.ofNullable(python) + /** + * Sets [Builder.metadata] to an arbitrary JSON value. + * + * You should usually call [Builder.metadata] with a well-typed [Metadata] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun metadata(metadata: JsonField) = apply { this.metadata = metadata } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun scoreModel(): Optional = Optional.ofNullable(scoreModel) + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - fun isLabelModel(): Boolean = labelModel != null + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - fun isStringCheck(): Boolean = stringCheck != null + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } - fun isTextSimilarity(): Boolean = textSimilarity != null + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - fun isPython(): Boolean = python != null + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - fun isScoreModel(): Boolean = scoreModel != null + /** + * Returns an immutable instance of [Logs]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .schema() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Logs = + Logs( + checkRequired("schema", schema), + type, + metadata, + additionalProperties.toMutableMap(), + ) + } - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - fun asLabelModel(): EvalLabelModelGrader = labelModel.getOrThrow("labelModel") + private var validated: Boolean = false - /** - * A StringCheckGrader object that performs a string comparison between input and reference - * using a specified operation. - */ - fun asStringCheck(): EvalStringCheckGrader = stringCheck.getOrThrow("stringCheck") + fun validate(): Logs = apply { + if (validated) { + return@apply + } - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun asTextSimilarity(): EvalTextSimilarityGrader = - textSimilarity.getOrThrow("textSimilarity") + schema().validate() + _type().let { + if (it != JsonValue.from("logs")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + metadata().ifPresent { it.validate() } + validated = true + } - /** A PythonGrader object that runs a python script on the input. */ - fun asPython(): Python = python.getOrThrow("python") + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun asScoreModel(): ScoreModel = scoreModel.getOrThrow("scoreModel") + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (schema.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("logs")) 1 else 0 } + + (metadata.asKnown().getOrNull()?.validity() ?: 0) - fun _json(): Optional = Optional.ofNullable(_json) + /** + * The json schema for the run data source items. Learn how to build JSON schemas + * [here](https://json-schema.org/). + */ + class Schema + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { - fun accept(visitor: Visitor): T = - when { - labelModel != null -> visitor.visitLabelModel(labelModel) - stringCheck != null -> visitor.visitStringCheck(stringCheck) - textSimilarity != null -> visitor.visitTextSimilarity(textSimilarity) - python != null -> visitor.visitPython(python) - scoreModel != null -> visitor.visitScoreModel(scoreModel) - else -> visitor.unknown(_json) - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties - private var validated: Boolean = false + fun toBuilder() = Builder().from(this) - fun validate(): TestingCriterion = apply { - if (validated) { - return@apply - } + companion object { - accept( - object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) { - labelModel.validate() + /** Returns a mutable builder for constructing an instance of [Schema]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Schema]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(schema: Schema) = apply { + additionalProperties = schema.additionalProperties.toMutableMap() } - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) { - stringCheck.validate() + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) } - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) { - textSimilarity.validate() + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) } - override fun visitPython(python: Python) { - python.validate() + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) } - override fun visitScoreModel(scoreModel: ScoreModel) { - scoreModel.validate() + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) } + + /** + * Returns an immutable instance of [Schema]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Schema = Schema(additionalProperties.toImmutable()) } - ) - validated = true - } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + private var validated: Boolean = false - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) = - labelModel.validity() + fun validate(): Schema = apply { + if (validated) { + return@apply + } - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) = - stringCheck.validity() + validated = true + } - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - textSimilarity.validity() + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - override fun visitPython(python: Python) = python.validity() + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> + !value.isNull() && !value.isMissing() + } - override fun visitScoreModel(scoreModel: ScoreModel) = scoreModel.validity() + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - override fun unknown(json: JsonValue?) = 0 + return /* spotless:off */ other is Schema && additionalProperties == other.additionalProperties /* spotless:on */ } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - return /* spotless:off */ other is TestingCriterion && labelModel == other.labelModel && stringCheck == other.stringCheck && textSimilarity == other.textSimilarity && python == other.python && scoreModel == other.scoreModel /* spotless:on */ - } + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ - override fun hashCode(): Int = /* spotless:off */ Objects.hash(labelModel, stringCheck, textSimilarity, python, scoreModel) /* spotless:on */ + override fun hashCode(): Int = hashCode - override fun toString(): String = - when { - labelModel != null -> "TestingCriterion{labelModel=$labelModel}" - stringCheck != null -> "TestingCriterion{stringCheck=$stringCheck}" - textSimilarity != null -> "TestingCriterion{textSimilarity=$textSimilarity}" - python != null -> "TestingCriterion{python=$python}" - scoreModel != null -> "TestingCriterion{scoreModel=$scoreModel}" - _json != null -> "TestingCriterion{_unknown=$_json}" - else -> throw IllegalStateException("Invalid TestingCriterion") + override fun toString() = "Schema{additionalProperties=$additionalProperties}" } - companion object { - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. + * Set of 16 key-value pairs that can be attached to an object. This can be useful for + * storing additional information about the object in a structured format, and querying + * for objects via API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with a + * maximum length of 512 characters. */ - @JvmStatic - fun ofLabelModel(labelModel: EvalLabelModelGrader) = - TestingCriterion(labelModel = labelModel) + class Metadata + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { - /** - * A StringCheckGrader object that performs a string comparison between input and - * reference using a specified operation. - */ - @JvmStatic - fun ofStringCheck(stringCheck: EvalStringCheckGrader) = - TestingCriterion(stringCheck = stringCheck) + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - @JvmStatic - fun ofTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - TestingCriterion(textSimilarity = textSimilarity) + fun toBuilder() = Builder().from(this) - /** A PythonGrader object that runs a python script on the input. */ - @JvmStatic fun ofPython(python: Python) = TestingCriterion(python = python) + companion object { - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - @JvmStatic - fun ofScoreModel(scoreModel: ScoreModel) = TestingCriterion(scoreModel = scoreModel) - } + /** Returns a mutable builder for constructing an instance of [Metadata]. */ + @JvmStatic fun builder() = Builder() + } - /** - * An interface that defines how to map each variant of [TestingCriterion] to a value of - * type [T]. - */ - interface Visitor { + /** A builder for [Metadata]. */ + class Builder internal constructor() { - /** - * A LabelModelGrader object which uses a model to assign labels to each item in the - * evaluation. - */ - fun visitLabelModel(labelModel: EvalLabelModelGrader): T + private var additionalProperties: MutableMap = mutableMapOf() - /** - * A StringCheckGrader object that performs a string comparison between input and - * reference using a specified operation. - */ - fun visitStringCheck(stringCheck: EvalStringCheckGrader): T + @JvmSynthetic + internal fun from(metadata: Metadata) = apply { + additionalProperties = metadata.additionalProperties.toMutableMap() + } - /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader): T + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - /** A PythonGrader object that runs a python script on the input. */ - fun visitPython(python: Python): T + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun visitScoreModel(scoreModel: ScoreModel): T + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } - /** - * Maps an unknown variant of [TestingCriterion] to a value of type [T]. - * - * An instance of [TestingCriterion] 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 TestingCriterion: $json") - } - } + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - internal class Deserializer : BaseDeserializer(TestingCriterion::class) { + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - override fun ObjectCodec.deserialize(node: JsonNode): TestingCriterion { - val json = JsonValue.fromJsonNode(node) - val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() + /** + * Returns an immutable instance of [Metadata]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Metadata = Metadata(additionalProperties.toImmutable()) + } - when (type) { - "label_model" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(labelModel = it, _json = json) - } ?: TestingCriterion(_json = json) - } - "string_check" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(stringCheck = it, _json = json) - } ?: TestingCriterion(_json = json) + private var validated: Boolean = false + + fun validate(): Metadata = apply { + if (validated) { + return@apply } - "text_similarity" -> { - return tryDeserialize(node, jacksonTypeRef()) - ?.let { TestingCriterion(textSimilarity = it, _json = json) } - ?: TestingCriterion(_json = json) + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false } - "python" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(python = it, _json = json) - } ?: TestingCriterion(_json = json) + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> + !value.isNull() && !value.isMissing() } - "score_model" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - TestingCriterion(scoreModel = it, _json = json) - } ?: TestingCriterion(_json = json) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } + + return /* spotless:off */ other is Metadata && additionalProperties == other.additionalProperties /* spotless:on */ } - return TestingCriterion(_json = json) - } - } + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ - internal class Serializer : BaseSerializer(TestingCriterion::class) { + override fun hashCode(): Int = hashCode - override fun serialize( - value: TestingCriterion, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.labelModel != null -> generator.writeObject(value.labelModel) - value.stringCheck != null -> generator.writeObject(value.stringCheck) - value.textSimilarity != null -> generator.writeObject(value.textSimilarity) - value.python != null -> generator.writeObject(value.python) - value.scoreModel != null -> generator.writeObject(value.scoreModel) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid TestingCriterion") + override fun toString() = "Metadata{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } + + return /* spotless:off */ other is Logs && schema == other.schema && type == other.type && metadata == other.metadata && additionalProperties == other.additionalProperties /* spotless:on */ } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(schema, type, metadata, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Logs{schema=$schema, type=$type, metadata=$metadata, additionalProperties=$additionalProperties}" } + } - /** A PythonGrader object that runs a python script on the input. */ - class Python - private constructor( - private val name: JsonField, - private val source: JsonField, - private val type: JsonValue, - private val imageTag: JsonField, - private val passThreshold: JsonField, - private val additionalProperties: MutableMap, - ) { + /** + * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing + * additional information about the object in a structured format, and querying for objects via + * API or the dashboard. + * + * Keys are strings with a maximum length of 64 characters. Values are strings with a maximum + * length of 512 characters. + */ + class Metadata + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { - @JsonCreator - private constructor( - @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), - @JsonProperty("source") - @ExcludeMissing - source: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - @JsonProperty("image_tag") - @ExcludeMissing - imageTag: JsonField = JsonMissing.of(), - @JsonProperty("pass_threshold") - @ExcludeMissing - passThreshold: JsonField = JsonMissing.of(), - ) : this(name, source, type, imageTag, passThreshold, mutableMapOf()) + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties - /** - * The name of the grader. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun name(): String = name.getRequired("name") + fun toBuilder() = Builder().from(this) - /** - * The source code of the python script. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun source(): String = source.getRequired("source") + companion object { - /** - * The object type, which is always `python`. - * - * Expected to always return the following: - * ```java - * JsonValue.from("python") - * ``` - * - * However, this method can be useful for debugging and logging (e.g. if the server - * responded with an unexpected value). - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + /** Returns a mutable builder for constructing an instance of [Metadata]. */ + @JvmStatic fun builder() = Builder() + } - /** - * The image tag to use for the python script. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun imageTag(): Optional = imageTag.getOptional("image_tag") + /** A builder for [Metadata]. */ + class Builder internal constructor() { - /** - * The threshold for the score. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + private var additionalProperties: MutableMap = mutableMapOf() - /** - * Returns the raw JSON value of [name]. - * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JvmSynthetic + internal fun from(metadata: Metadata) = apply { + additionalProperties = metadata.additionalProperties.toMutableMap() + } - /** - * Returns the raw JSON value of [source]. - * - * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - /** - * Returns the raw JSON value of [imageTag]. - * - * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag + 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) + } /** - * Returns the raw JSON value of [passThreshold]. + * Returns an immutable instance of [Metadata]. * - * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected - * type. + * Further updates to this [Builder] will not mutate the returned instance. */ - @JsonProperty("pass_threshold") - @ExcludeMissing - fun _passThreshold(): JsonField = passThreshold + fun build(): Metadata = Metadata(additionalProperties.toImmutable()) + } - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) + private var validated: Boolean = false + + fun validate(): Metadata = apply { + if (validated) { + return@apply } - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) + validated = true + } - fun toBuilder() = Builder().from(this) + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - companion object { + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } - /** - * Returns a mutable builder for constructing an instance of [Python]. - * - * The following fields are required: - * ```java - * .name() - * .source() - * ``` - */ - @JvmStatic fun builder() = Builder() + override fun equals(other: Any?): Boolean { + if (this === other) { + return true } - /** A builder for [Python]. */ - class Builder internal constructor() { + return /* spotless:off */ other is Metadata && additionalProperties == other.additionalProperties /* spotless:on */ + } - private var name: JsonField? = null - private var source: JsonField? = null - private var type: JsonValue = JsonValue.from("python") - private var imageTag: JsonField = JsonMissing.of() - private var passThreshold: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ - @JvmSynthetic - internal fun from(python: Python) = apply { - name = python.name - source = python.source - type = python.type - imageTag = python.imageTag - passThreshold = python.passThreshold - additionalProperties = python.additionalProperties.toMutableMap() - } + override fun hashCode(): Int = hashCode - /** The name of the grader. */ - fun name(name: String) = name(JsonField.of(name)) + override fun toString() = "Metadata{additionalProperties=$additionalProperties}" + } - /** - * Sets [Builder.name] to an arbitrary JSON value. - * - * You should usually call [Builder.name] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun name(name: JsonField) = apply { this.name = name } + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the evaluation. + */ + @JsonDeserialize(using = TestingCriterion.Deserializer::class) + @JsonSerialize(using = TestingCriterion.Serializer::class) + class TestingCriterion + private constructor( + private val labelModelGrader: LabelModelGrader? = null, + private val stringCheckGrader: StringCheckGrader? = null, + private val evalGraderTextSimilarity: EvalGraderTextSimilarity? = null, + private val evalGraderPython: EvalGraderPython? = null, + private val evalGraderScoreModel: EvalGraderScoreModel? = null, + private val _json: JsonValue? = null, + ) { - /** The source code of the python script. */ - fun source(source: String) = source(JsonField.of(source)) + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + fun labelModelGrader(): Optional = Optional.ofNullable(labelModelGrader) - /** - * Sets [Builder.source] to an arbitrary JSON value. - * - * You should usually call [Builder.source] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun source(source: JsonField) = apply { this.source = source } + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun stringCheckGrader(): Optional = + Optional.ofNullable(stringCheckGrader) - /** - * Sets the field to an arbitrary JSON value. - * - * It is usually unnecessary to call this method because the field defaults to the - * following: - * ```java - * JsonValue.from("python") - * ``` - * - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun type(type: JsonValue) = apply { this.type = type } + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun evalGraderTextSimilarity(): Optional = + Optional.ofNullable(evalGraderTextSimilarity) - /** The image tag to use for the python script. */ - fun imageTag(imageTag: String) = imageTag(JsonField.of(imageTag)) + /** A PythonGrader object that runs a python script on the input. */ + fun evalGraderPython(): Optional = Optional.ofNullable(evalGraderPython) - /** - * Sets [Builder.imageTag] to an arbitrary JSON value. - * - * You should usually call [Builder.imageTag] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun imageTag(imageTag: JsonField) = apply { this.imageTag = imageTag } + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun evalGraderScoreModel(): Optional = + Optional.ofNullable(evalGraderScoreModel) - /** The threshold for the score. */ - fun passThreshold(passThreshold: Double) = - passThreshold(JsonField.of(passThreshold)) + fun isLabelModelGrader(): Boolean = labelModelGrader != null - /** - * Sets [Builder.passThreshold] to an arbitrary JSON value. - * - * You should usually call [Builder.passThreshold] with a well-typed [Double] value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun passThreshold(passThreshold: JsonField) = apply { - this.passThreshold = passThreshold - } + fun isStringCheckGrader(): Boolean = stringCheckGrader != null - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + fun isEvalGraderTextSimilarity(): Boolean = evalGraderTextSimilarity != null - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + fun isEvalGraderPython(): Boolean = evalGraderPython != null - fun putAllAdditionalProperties(additionalProperties: Map) = - apply { - this.additionalProperties.putAll(additionalProperties) - } + fun isEvalGraderScoreModel(): Boolean = evalGraderScoreModel != null - fun removeAdditionalProperty(key: String) = apply { - additionalProperties.remove(key) - } + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + fun asLabelModelGrader(): LabelModelGrader = labelModelGrader.getOrThrow("labelModelGrader") - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun asStringCheckGrader(): StringCheckGrader = + stringCheckGrader.getOrThrow("stringCheckGrader") - /** - * Returns an immutable instance of [Python]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .name() - * .source() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): Python = - Python( - checkRequired("name", name), - checkRequired("source", source), - type, - imageTag, - passThreshold, - additionalProperties.toMutableMap(), - ) + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun asEvalGraderTextSimilarity(): EvalGraderTextSimilarity = + evalGraderTextSimilarity.getOrThrow("evalGraderTextSimilarity") + + /** A PythonGrader object that runs a python script on the input. */ + fun asEvalGraderPython(): EvalGraderPython = evalGraderPython.getOrThrow("evalGraderPython") + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun asEvalGraderScoreModel(): EvalGraderScoreModel = + evalGraderScoreModel.getOrThrow("evalGraderScoreModel") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + labelModelGrader != null -> visitor.visitLabelModelGrader(labelModelGrader) + stringCheckGrader != null -> visitor.visitStringCheckGrader(stringCheckGrader) + evalGraderTextSimilarity != null -> + visitor.visitEvalGraderTextSimilarity(evalGraderTextSimilarity) + evalGraderPython != null -> visitor.visitEvalGraderPython(evalGraderPython) + evalGraderScoreModel != null -> + visitor.visitEvalGraderScoreModel(evalGraderScoreModel) + else -> visitor.unknown(_json) } - private var validated: Boolean = false + private var validated: Boolean = false - fun validate(): Python = apply { - if (validated) { - return@apply - } + fun validate(): TestingCriterion = apply { + if (validated) { + return@apply + } - name() - source() - _type().let { - if (it != JsonValue.from("python")) { - throw OpenAIInvalidDataException("'type' is invalid, received $it") + accept( + object : Visitor { + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) { + labelModelGrader.validate() + } + + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) { + stringCheckGrader.validate() + } + + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) { + evalGraderTextSimilarity.validate() + } + + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) { + evalGraderPython.validate() + } + + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) { + evalGraderScoreModel.validate() } } - imageTag() - passThreshold() - validated = true + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) = + labelModelGrader.validity() + + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) = + stringCheckGrader.validity() + + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) = evalGraderTextSimilarity.validity() + + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) = + evalGraderPython.validity() + + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) = evalGraderScoreModel.validity() + + override fun unknown(json: JsonValue?) = 0 } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is TestingCriterion && labelModelGrader == other.labelModelGrader && stringCheckGrader == other.stringCheckGrader && evalGraderTextSimilarity == other.evalGraderTextSimilarity && evalGraderPython == other.evalGraderPython && evalGraderScoreModel == other.evalGraderScoreModel /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(labelModelGrader, stringCheckGrader, evalGraderTextSimilarity, evalGraderPython, evalGraderScoreModel) /* spotless:on */ + + override fun toString(): String = + when { + labelModelGrader != null -> "TestingCriterion{labelModelGrader=$labelModelGrader}" + stringCheckGrader != null -> + "TestingCriterion{stringCheckGrader=$stringCheckGrader}" + evalGraderTextSimilarity != null -> + "TestingCriterion{evalGraderTextSimilarity=$evalGraderTextSimilarity}" + evalGraderPython != null -> "TestingCriterion{evalGraderPython=$evalGraderPython}" + evalGraderScoreModel != null -> + "TestingCriterion{evalGraderScoreModel=$evalGraderScoreModel}" + _json != null -> "TestingCriterion{_unknown=$_json}" + else -> throw IllegalStateException("Invalid TestingCriterion") + } + + companion object { + + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + @JvmStatic + fun ofLabelModelGrader(labelModelGrader: LabelModelGrader) = + TestingCriterion(labelModelGrader = labelModelGrader) + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + @JvmStatic + fun ofStringCheckGrader(stringCheckGrader: StringCheckGrader) = + TestingCriterion(stringCheckGrader = stringCheckGrader) + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + @JvmStatic + fun ofEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity) = + TestingCriterion(evalGraderTextSimilarity = evalGraderTextSimilarity) + + /** A PythonGrader object that runs a python script on the input. */ + @JvmStatic + fun ofEvalGraderPython(evalGraderPython: EvalGraderPython) = + TestingCriterion(evalGraderPython = evalGraderPython) + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + @JvmStatic + fun ofEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel) = + TestingCriterion(evalGraderScoreModel = evalGraderScoreModel) + } + + /** + * An interface that defines how to map each variant of [TestingCriterion] to a value of + * type [T]. + */ + interface Visitor { + + /** + * A LabelModelGrader object which uses a model to assign labels to each item in the + * evaluation. + */ + fun visitLabelModelGrader(labelModelGrader: LabelModelGrader): T + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader): T + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun visitEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity): T + + /** A PythonGrader object that runs a python script on the input. */ + fun visitEvalGraderPython(evalGraderPython: EvalGraderPython): T + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun visitEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel): T /** - * Returns a score indicating how many valid values are contained in this object - * recursively. + * Maps an unknown variant of [TestingCriterion] to a value of type [T]. * - * Used for best match union deserialization. + * An instance of [TestingCriterion] 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. */ - @JvmSynthetic - internal fun validity(): Int = - (if (name.asKnown().isPresent) 1 else 0) + - (if (source.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("python")) 1 else 0 } + - (if (imageTag.asKnown().isPresent) 1 else 0) + - (if (passThreshold.asKnown().isPresent) 1 else 0) + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown TestingCriterion: $json") + } + } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + internal class Deserializer : BaseDeserializer(TestingCriterion::class) { - return /* spotless:off */ other is Python && name == other.name && source == other.source && type == other.type && imageTag == other.imageTag && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ - } + override fun ObjectCodec.deserialize(node: JsonNode): TestingCriterion { + val json = JsonValue.fromJsonNode(node) - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, additionalProperties) } - /* spotless:on */ + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(labelModelGrader = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(stringCheckGrader = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(evalGraderTextSimilarity = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(evalGraderPython = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(evalGraderScoreModel = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> TestingCriterion(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } - override fun hashCode(): Int = hashCode + internal class Serializer : BaseSerializer(TestingCriterion::class) { - override fun toString() = - "Python{name=$name, source=$source, type=$type, imageTag=$imageTag, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" + override fun serialize( + value: TestingCriterion, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.labelModelGrader != null -> generator.writeObject(value.labelModelGrader) + value.stringCheckGrader != null -> + generator.writeObject(value.stringCheckGrader) + value.evalGraderTextSimilarity != null -> + generator.writeObject(value.evalGraderTextSimilarity) + value.evalGraderPython != null -> generator.writeObject(value.evalGraderPython) + value.evalGraderScoreModel != null -> + generator.writeObject(value.evalGraderScoreModel) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid TestingCriterion") + } + } } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - class ScoreModel + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + class EvalGraderTextSimilarity private constructor( - private val input: JsonField>, - private val model: JsonField, + private val evaluationMetric: JsonField, + private val input: JsonField, private val name: JsonField, + private val reference: JsonField, private val type: JsonValue, private val passThreshold: JsonField, - private val range: JsonField>, - private val samplingParams: JsonValue, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("input") + @JsonProperty("evaluation_metric") @ExcludeMissing - input: JsonField> = JsonMissing.of(), - @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + evaluationMetric: JsonField = + JsonMissing.of(), + @JsonProperty("input") @ExcludeMissing input: JsonField = JsonMissing.of(), @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("reference") + @ExcludeMissing + reference: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("pass_threshold") @ExcludeMissing passThreshold: JsonField = JsonMissing.of(), - @JsonProperty("range") - @ExcludeMissing - range: JsonField> = JsonMissing.of(), - @JsonProperty("sampling_params") - @ExcludeMissing - samplingParams: JsonValue = JsonMissing.of(), - ) : this(input, model, name, type, passThreshold, range, samplingParams, mutableMapOf()) + ) : this(evaluationMetric, input, name, reference, type, passThreshold, mutableMapOf()) + + fun toTextSimilarityGrader(): TextSimilarityGrader = + TextSimilarityGrader.builder() + .evaluationMetric(evaluationMetric) + .input(input) + .name(name) + .reference(reference) + .type(type) + .build() /** - * The input text. This may include template strings. + * The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, + * `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun input(): List = input.getRequired("input") + fun evaluationMetric(): TextSimilarityGrader.EvaluationMetric = + evaluationMetric.getRequired("evaluation_metric") /** - * The model to use for the evaluation. + * The text being graded. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected * value). */ - fun model(): String = model.getRequired("model") + fun input(): String = input.getRequired("input") /** * The name of the grader. @@ -1534,11 +1797,20 @@ private constructor( fun name(): String = name.getRequired("name") /** - * The object type, which is always `score_model`. + * The text being graded against. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun reference(): String = reference.getRequired("reference") + + /** + * The type of grader. * * Expected to always return the following: * ```java - * JsonValue.from("score_model") + * JsonValue.from("text_similarity") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1549,44 +1821,46 @@ private constructor( /** * The threshold for the score. * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). */ - fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + fun passThreshold(): Double = passThreshold.getRequired("pass_threshold") /** - * The range of the score. Defaults to `[0, 1]`. + * Returns the raw JSON value of [evaluationMetric]. * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). + * Unlike [evaluationMetric], this method doesn't throw if the JSON field has an + * unexpected type. */ - fun range(): Optional> = range.getOptional("range") - - /** The sampling parameters for the model. */ - @JsonProperty("sampling_params") + @JsonProperty("evaluation_metric") @ExcludeMissing - fun _samplingParams(): JsonValue = samplingParams + fun _evaluationMetric(): JsonField = + evaluationMetric /** * Returns the raw JSON value of [input]. * * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("input") @ExcludeMissing fun _input(): JsonField> = input + @JsonProperty("input") @ExcludeMissing fun _input(): JsonField = input /** - * Returns the raw JSON value of [model]. + * Returns the raw JSON value of [name]. * - * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name /** - * Returns the raw JSON value of [name]. + * Returns the raw JSON value of [reference]. * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected + * type. */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JsonProperty("reference") + @ExcludeMissing + fun _reference(): JsonField = reference /** * Returns the raw JSON value of [passThreshold]. @@ -1598,13 +1872,6 @@ private constructor( @ExcludeMissing fun _passThreshold(): JsonField = passThreshold - /** - * Returns the raw JSON value of [range]. - * - * Unlike [range], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("range") @ExcludeMissing fun _range(): JsonField> = range - @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { additionalProperties.put(key, value) @@ -1620,79 +1887,74 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [ScoreModel]. + * Returns a mutable builder for constructing an instance of + * [EvalGraderTextSimilarity]. * * The following fields are required: * ```java + * .evaluationMetric() * .input() - * .model() * .name() + * .reference() + * .passThreshold() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [ScoreModel]. */ + /** A builder for [EvalGraderTextSimilarity]. */ class Builder internal constructor() { - private var input: JsonField>? = null - private var model: JsonField? = null + private var evaluationMetric: JsonField? = + null + private var input: JsonField? = null private var name: JsonField? = null - private var type: JsonValue = JsonValue.from("score_model") - private var passThreshold: JsonField = JsonMissing.of() - private var range: JsonField>? = null - private var samplingParams: JsonValue = JsonMissing.of() + private var reference: JsonField? = null + private var type: JsonValue = JsonValue.from("text_similarity") + private var passThreshold: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(scoreModel: ScoreModel) = apply { - input = scoreModel.input.map { it.toMutableList() } - model = scoreModel.model - name = scoreModel.name - type = scoreModel.type - passThreshold = scoreModel.passThreshold - range = scoreModel.range.map { it.toMutableList() } - samplingParams = scoreModel.samplingParams - additionalProperties = scoreModel.additionalProperties.toMutableMap() + internal fun from(evalGraderTextSimilarity: EvalGraderTextSimilarity) = apply { + evaluationMetric = evalGraderTextSimilarity.evaluationMetric + input = evalGraderTextSimilarity.input + name = evalGraderTextSimilarity.name + reference = evalGraderTextSimilarity.reference + type = evalGraderTextSimilarity.type + passThreshold = evalGraderTextSimilarity.passThreshold + additionalProperties = + evalGraderTextSimilarity.additionalProperties.toMutableMap() } - /** The input text. This may include template strings. */ - fun input(input: List) = input(JsonField.of(input)) - /** - * Sets [Builder.input] to an arbitrary JSON value. - * - * You should usually call [Builder.input] with a well-typed `List` value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. + * The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, + * `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. */ - fun input(input: JsonField>) = apply { - this.input = input.map { it.toMutableList() } - } + fun evaluationMetric(evaluationMetric: TextSimilarityGrader.EvaluationMetric) = + evaluationMetric(JsonField.of(evaluationMetric)) /** - * Adds a single [Input] to [Builder.input]. + * Sets [Builder.evaluationMetric] to an arbitrary JSON value. * - * @throws IllegalStateException if the field was previously set to a non-list. + * You should usually call [Builder.evaluationMetric] with a well-typed + * [TextSimilarityGrader.EvaluationMetric] value instead. This method is primarily + * for setting the field to an undocumented or not yet supported value. */ - fun addInput(input: Input) = apply { - this.input = - (this.input ?: JsonField.of(mutableListOf())).also { - checkKnown("input", it).add(input) - } - } + fun evaluationMetric( + evaluationMetric: JsonField + ) = apply { this.evaluationMetric = evaluationMetric } - /** The model to use for the evaluation. */ - fun model(model: String) = model(JsonField.of(model)) + /** The text being graded. */ + fun input(input: String) = input(JsonField.of(input)) /** - * Sets [Builder.model] to an arbitrary JSON value. + * Sets [Builder.input] to an arbitrary JSON value. * - * You should usually call [Builder.model] with a well-typed [String] value instead. + * You should usually call [Builder.input] with a well-typed [String] value instead. * This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun model(model: JsonField) = apply { this.model = model } + fun input(input: JsonField) = apply { this.input = input } /** The name of the grader. */ fun name(name: String) = name(JsonField.of(name)) @@ -1706,13 +1968,25 @@ private constructor( */ fun name(name: JsonField) = apply { this.name = name } + /** The text being graded against. */ + fun reference(reference: String) = reference(JsonField.of(reference)) + + /** + * Sets [Builder.reference] to an arbitrary JSON value. + * + * You should usually call [Builder.reference] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun reference(reference: JsonField) = apply { this.reference = reference } + /** * Sets the field to an arbitrary JSON value. * * It is usually unnecessary to call this method because the field defaults to the * following: * ```java - * JsonValue.from("score_model") + * JsonValue.from("text_similarity") * ``` * * This method is primarily for setting the field to an undocumented or not yet @@ -1735,37 +2009,6 @@ private constructor( this.passThreshold = passThreshold } - /** The range of the score. Defaults to `[0, 1]`. */ - fun range(range: List) = range(JsonField.of(range)) - - /** - * Sets [Builder.range] to an arbitrary JSON value. - * - * You should usually call [Builder.range] with a well-typed `List` value - * instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. - */ - fun range(range: JsonField>) = apply { - this.range = range.map { it.toMutableList() } - } - - /** - * Adds a single [Double] to [Builder.range]. - * - * @throws IllegalStateException if the field was previously set to a non-list. - */ - fun addRange(range: Double) = apply { - this.range = - (this.range ?: JsonField.of(mutableListOf())).also { - checkKnown("range", it).add(range) - } - } - - /** The sampling parameters for the model. */ - fun samplingParams(samplingParams: JsonValue) = apply { - this.samplingParams = samplingParams - } - fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() putAllAdditionalProperties(additionalProperties) @@ -1789,1069 +2032,835 @@ private constructor( } /** - * Returns an immutable instance of [ScoreModel]. + * Returns an immutable instance of [EvalGraderTextSimilarity]. * * Further updates to this [Builder] will not mutate the returned instance. * * The following fields are required: * ```java + * .evaluationMetric() * .input() - * .model() * .name() + * .reference() + * .passThreshold() * ``` * * @throws IllegalStateException if any required field is unset. */ - fun build(): ScoreModel = - ScoreModel( - checkRequired("input", input).map { it.toImmutable() }, - checkRequired("model", model), + fun build(): EvalGraderTextSimilarity = + EvalGraderTextSimilarity( + checkRequired("evaluationMetric", evaluationMetric), + checkRequired("input", input), checkRequired("name", name), + checkRequired("reference", reference), type, - passThreshold, - (range ?: JsonMissing.of()).map { it.toImmutable() }, - samplingParams, + checkRequired("passThreshold", passThreshold), additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): ScoreModel = apply { + fun validate(): EvalGraderTextSimilarity = apply { if (validated) { return@apply } - input().forEach { it.validate() } - model() + evaluationMetric().validate() + input() name() + reference() _type().let { - if (it != JsonValue.from("score_model")) { - throw OpenAIInvalidDataException("'type' is invalid, received $it") - } - } - passThreshold() - range() - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (input.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + - (if (model.asKnown().isPresent) 1 else 0) + - (if (name.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("score_model")) 1 else 0 } + - (if (passThreshold.asKnown().isPresent) 1 else 0) + - (range.asKnown().getOrNull()?.size ?: 0) - - /** - * A message input to the model with a role indicating instruction following hierarchy. - * Instructions given with the `developer` or `system` role take precedence over - * instructions given with the `user` role. Messages with the `assistant` role are - * presumed to have been generated by the model in previous interactions. - */ - class Input - private constructor( - private val content: JsonField, - private val role: JsonField, - private val type: JsonField, - private val additionalProperties: MutableMap, - ) { - - @JsonCreator - private constructor( - @JsonProperty("content") - @ExcludeMissing - content: JsonField = JsonMissing.of(), - @JsonProperty("role") @ExcludeMissing role: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), - ) : this(content, role, type, mutableMapOf()) - - /** - * Text inputs to the model - can contain template strings. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun content(): Content = content.getRequired("content") - - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected - * value). - */ - fun role(): Role = role.getRequired("role") - - /** - * The type of the message input. Always `message`. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun type(): Optional = type.getOptional("type") - - /** - * Returns the raw JSON value of [content]. - * - * Unlike [content], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("content") - @ExcludeMissing - fun _content(): JsonField = content - - /** - * Returns the raw JSON value of [role]. - * - * Unlike [role], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("role") @ExcludeMissing fun _role(): JsonField = role - - /** - * Returns the raw JSON value of [type]. - * - * Unlike [type], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { + if (it != JsonValue.from("text_similarity")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + passThreshold() + validated = true + } - /** - * Returns a mutable builder for constructing an instance of [Input]. - * - * The following fields are required: - * ```java - * .content() - * .role() - * ``` - */ - @JvmStatic fun builder() = Builder() + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false } - /** A builder for [Input]. */ - class Builder internal constructor() { + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (evaluationMetric.asKnown().getOrNull()?.validity() ?: 0) + + (if (input.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (reference.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("text_similarity")) 1 else 0 } + + (if (passThreshold.asKnown().isPresent) 1 else 0) - private var content: JsonField? = null - private var role: JsonField? = null - private var type: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - @JvmSynthetic - internal fun from(input: Input) = apply { - content = input.content - role = input.role - type = input.type - additionalProperties = input.additionalProperties.toMutableMap() - } + return /* spotless:off */ other is EvalGraderTextSimilarity && evaluationMetric == other.evaluationMetric && input == other.input && name == other.name && reference == other.reference && type == other.type && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ + } - /** Text inputs to the model - can contain template strings. */ - fun content(content: Content) = content(JsonField.of(content)) + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(evaluationMetric, input, name, reference, type, passThreshold, additionalProperties) } + /* spotless:on */ - /** - * Sets [Builder.content] to an arbitrary JSON value. - * - * You should usually call [Builder.content] with a well-typed [Content] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun content(content: JsonField) = apply { this.content = content } + override fun hashCode(): Int = hashCode - /** Alias for calling [content] with `Content.ofTextInput(textInput)`. */ - fun content(textInput: String) = content(Content.ofTextInput(textInput)) + override fun toString() = + "EvalGraderTextSimilarity{evaluationMetric=$evaluationMetric, input=$input, name=$name, reference=$reference, type=$type, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" + } - /** - * Alias for calling [content] with - * `Content.ofResponseInputText(responseInputText)`. - */ - fun content(responseInputText: ResponseInputText) = - content(Content.ofResponseInputText(responseInputText)) + /** A PythonGrader object that runs a python script on the input. */ + class EvalGraderPython + private constructor( + private val name: JsonField, + private val source: JsonField, + private val type: JsonValue, + private val imageTag: JsonField, + private val passThreshold: JsonField, + private val additionalProperties: MutableMap, + ) { - /** Alias for calling [content] with `Content.ofOutputText(outputText)`. */ - fun content(outputText: Content.OutputText) = - content(Content.ofOutputText(outputText)) + @JsonCreator + private constructor( + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("source") + @ExcludeMissing + source: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("image_tag") + @ExcludeMissing + imageTag: JsonField = JsonMissing.of(), + @JsonProperty("pass_threshold") + @ExcludeMissing + passThreshold: JsonField = JsonMissing.of(), + ) : this(name, source, type, imageTag, passThreshold, mutableMapOf()) - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - */ - fun role(role: Role) = role(JsonField.of(role)) + fun toPythonGrader(): PythonGrader = + PythonGrader.builder() + .name(name) + .source(source) + .type(type) + .imageTag(imageTag) + .build() - /** - * Sets [Builder.role] to an arbitrary JSON value. - * - * You should usually call [Builder.role] with a well-typed [Role] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun role(role: JsonField) = apply { this.role = role } + /** + * The name of the grader. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun name(): String = name.getRequired("name") - /** The type of the message input. Always `message`. */ - fun type(type: Type) = type(JsonField.of(type)) + /** + * The source code of the python script. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun source(): String = source.getRequired("source") - /** - * Sets [Builder.type] to an arbitrary JSON value. - * - * You should usually call [Builder.type] with a well-typed [Type] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun type(type: JsonField) = apply { this.type = type } + /** + * The object type, which is always `python`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("python") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + /** + * The image tag to use for the python script. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun imageTag(): Optional = imageTag.getOptional("image_tag") - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + /** + * The threshold for the score. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") - fun putAllAdditionalProperties(additionalProperties: Map) = - apply { - this.additionalProperties.putAll(additionalProperties) - } + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name - fun removeAdditionalProperty(key: String) = apply { - additionalProperties.remove(key) - } + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + /** + * Returns the raw JSON value of [imageTag]. + * + * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag - /** - * Returns an immutable instance of [Input]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .content() - * .role() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): Input = - Input( - checkRequired("content", content), - checkRequired("role", role), - type, - additionalProperties.toMutableMap(), - ) - } + /** + * Returns the raw JSON value of [passThreshold]. + * + * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pass_threshold") + @ExcludeMissing + fun _passThreshold(): JsonField = passThreshold - private var validated: Boolean = false + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } - fun validate(): Input = apply { - if (validated) { - return@apply - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - content().validate() - role().validate() - type().ifPresent { it.validate() } - validated = true - } + fun toBuilder() = Builder().from(this) - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + companion object { /** - * Returns a score indicating how many valid values are contained in this object - * recursively. + * Returns a mutable builder for constructing an instance of [EvalGraderPython]. * - * Used for best match union deserialization. + * The following fields are required: + * ```java + * .name() + * .source() + * ``` */ - @JvmSynthetic - internal fun validity(): Int = - (content.asKnown().getOrNull()?.validity() ?: 0) + - (role.asKnown().getOrNull()?.validity() ?: 0) + - (type.asKnown().getOrNull()?.validity() ?: 0) - - /** Text inputs to the model - can contain template strings. */ - @JsonDeserialize(using = Content.Deserializer::class) - @JsonSerialize(using = Content.Serializer::class) - class Content - private constructor( - private val textInput: String? = null, - private val responseInputText: ResponseInputText? = null, - private val outputText: OutputText? = null, - private val _json: JsonValue? = null, - ) { - - /** A text input to the model. */ - fun textInput(): Optional = Optional.ofNullable(textInput) - - /** A text input to the model. */ - fun responseInputText(): Optional = - Optional.ofNullable(responseInputText) - - /** A text output from the model. */ - fun outputText(): Optional = Optional.ofNullable(outputText) - - fun isTextInput(): Boolean = textInput != null - - fun isResponseInputText(): Boolean = responseInputText != null - - fun isOutputText(): Boolean = outputText != null - - /** A text input to the model. */ - fun asTextInput(): String = textInput.getOrThrow("textInput") - - /** A text input to the model. */ - fun asResponseInputText(): ResponseInputText = - responseInputText.getOrThrow("responseInputText") + @JvmStatic fun builder() = Builder() + } - /** A text output from the model. */ - fun asOutputText(): OutputText = outputText.getOrThrow("outputText") + /** A builder for [EvalGraderPython]. */ + class Builder internal constructor() { - fun _json(): Optional = Optional.ofNullable(_json) + private var name: JsonField? = null + private var source: JsonField? = null + private var type: JsonValue = JsonValue.from("python") + private var imageTag: JsonField = JsonMissing.of() + private var passThreshold: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() - fun accept(visitor: Visitor): T = - when { - textInput != null -> visitor.visitTextInput(textInput) - responseInputText != null -> - visitor.visitResponseInputText(responseInputText) - outputText != null -> visitor.visitOutputText(outputText) - else -> visitor.unknown(_json) - } + @JvmSynthetic + internal fun from(evalGraderPython: EvalGraderPython) = apply { + name = evalGraderPython.name + source = evalGraderPython.source + type = evalGraderPython.type + imageTag = evalGraderPython.imageTag + passThreshold = evalGraderPython.passThreshold + additionalProperties = evalGraderPython.additionalProperties.toMutableMap() + } - private var validated: Boolean = false + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) - fun validate(): Content = apply { - if (validated) { - return@apply - } + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun name(name: JsonField) = apply { this.name = name } - accept( - object : Visitor { - override fun visitTextInput(textInput: String) {} + /** The source code of the python script. */ + fun source(source: String) = source(JsonField.of(source)) - override fun visitResponseInputText( - responseInputText: ResponseInputText - ) { - responseInputText.validate() - } + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun source(source: JsonField) = apply { this.source = source } - override fun visitOutputText(outputText: OutputText) { - outputText.validate() - } - } - ) - validated = true - } + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("python") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + /** The image tag to use for the python script. */ + fun imageTag(imageTag: String) = imageTag(JsonField.of(imageTag)) - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitTextInput(textInput: String) = 1 + /** + * Sets [Builder.imageTag] to an arbitrary JSON value. + * + * You should usually call [Builder.imageTag] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun imageTag(imageTag: JsonField) = apply { this.imageTag = imageTag } - override fun visitResponseInputText( - responseInputText: ResponseInputText - ) = responseInputText.validity() + /** The threshold for the score. */ + fun passThreshold(passThreshold: Double) = + passThreshold(JsonField.of(passThreshold)) - override fun visitOutputText(outputText: OutputText) = - outputText.validity() + /** + * Sets [Builder.passThreshold] to an arbitrary JSON value. + * + * You should usually call [Builder.passThreshold] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun passThreshold(passThreshold: JsonField) = apply { + this.passThreshold = passThreshold + } - override fun unknown(json: JsonValue?) = 0 - } - ) + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - return /* spotless:off */ other is Content && textInput == other.textInput && responseInputText == other.responseInputText && outputText == other.outputText /* spotless:on */ + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(textInput, responseInputText, outputText) /* spotless:on */ - - override fun toString(): String = - when { - textInput != null -> "Content{textInput=$textInput}" - responseInputText != null -> - "Content{responseInputText=$responseInputText}" - outputText != null -> "Content{outputText=$outputText}" - _json != null -> "Content{_unknown=$_json}" - else -> throw IllegalStateException("Invalid Content") - } - - companion object { + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - /** A text input to the model. */ - @JvmStatic - fun ofTextInput(textInput: String) = Content(textInput = textInput) + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - /** A text input to the model. */ - @JvmStatic - fun ofResponseInputText(responseInputText: ResponseInputText) = - Content(responseInputText = responseInputText) + /** + * Returns an immutable instance of [EvalGraderPython]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .name() + * .source() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): EvalGraderPython = + EvalGraderPython( + checkRequired("name", name), + checkRequired("source", source), + type, + imageTag, + passThreshold, + additionalProperties.toMutableMap(), + ) + } - /** A text output from the model. */ - @JvmStatic - fun ofOutputText(outputText: OutputText) = Content(outputText = outputText) - } + private var validated: Boolean = false - /** - * An interface that defines how to map each variant of [Content] to a value of - * type [T]. - */ - interface Visitor { - - /** A text input to the model. */ - fun visitTextInput(textInput: String): T - - /** A text input to the model. */ - fun visitResponseInputText(responseInputText: ResponseInputText): T - - /** A text output from the model. */ - fun visitOutputText(outputText: OutputText): T - - /** - * Maps an unknown variant of [Content] to a value of type [T]. - * - * An instance of [Content] 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 Content: $json") - } - } + fun validate(): EvalGraderPython = apply { + if (validated) { + return@apply + } - internal class Deserializer : BaseDeserializer(Content::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): Content { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { Content(responseInputText = it, _json = json) }, - tryDeserialize(node, jacksonTypeRef())?.let { - Content(outputText = it, _json = json) - }, - tryDeserialize(node, jacksonTypeRef())?.let { - Content(textInput = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from array). - 0 -> Content(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } + name() + source() + _type().let { + if (it != JsonValue.from("python")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") } + } + imageTag() + passThreshold() + validated = true + } - internal class Serializer : BaseSerializer(Content::class) { - - override fun serialize( - value: Content, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.textInput != null -> generator.writeObject(value.textInput) - value.responseInputText != null -> - generator.writeObject(value.responseInputText) - value.outputText != null -> generator.writeObject(value.outputText) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid Content") - } - } - } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - /** A text output from the model. */ - class OutputText - private constructor( - private val text: JsonField, - private val type: JsonValue, - private val additionalProperties: MutableMap, - ) { + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (name.asKnown().isPresent) 1 else 0) + + (if (source.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("python")) 1 else 0 } + + (if (imageTag.asKnown().isPresent) 1 else 0) + + (if (passThreshold.asKnown().isPresent) 1 else 0) - @JsonCreator - private constructor( - @JsonProperty("text") - @ExcludeMissing - text: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - ) : this(text, type, mutableMapOf()) - - /** - * The text output from the model. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected - * type or is unexpectedly missing or null (e.g. if the server responded - * with an unexpected value). - */ - fun text(): String = text.getRequired("text") - - /** - * The type of the output text. Always `output_text`. - * - * Expected to always return the following: - * ```java - * JsonValue.from("output_text") - * ``` - * - * However, this method can be useful for debugging and logging (e.g. if the - * server responded with an unexpected value). - */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - - /** - * Returns the raw JSON value of [text]. - * - * Unlike [text], this method doesn't throw if the JSON field has an - * unexpected type. - */ - @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { - - /** - * Returns a mutable builder for constructing an instance of - * [OutputText]. - * - * The following fields are required: - * ```java - * .text() - * ``` - */ - @JvmStatic fun builder() = Builder() - } + return /* spotless:off */ other is EvalGraderPython && name == other.name && source == other.source && type == other.type && imageTag == other.imageTag && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ + } - /** A builder for [OutputText]. */ - class Builder internal constructor() { - - private var text: JsonField? = null - private var type: JsonValue = JsonValue.from("output_text") - private var additionalProperties: MutableMap = - mutableMapOf() - - @JvmSynthetic - internal fun from(outputText: OutputText) = apply { - text = outputText.text - type = outputText.type - additionalProperties = - outputText.additionalProperties.toMutableMap() - } - - /** The text output from the model. */ - fun text(text: String) = text(JsonField.of(text)) - - /** - * Sets [Builder.text] to an arbitrary JSON value. - * - * You should usually call [Builder.text] with a well-typed [String] - * value instead. This method is primarily for setting the field to an - * undocumented or not yet supported value. - */ - fun text(text: JsonField) = apply { this.text = text } - - /** - * Sets the field to an arbitrary JSON value. - * - * It is usually unnecessary to call this method because the field - * defaults to the following: - * ```java - * JsonValue.from("output_text") - * ``` - * - * This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun type(type: JsonValue) = apply { this.type = type } - - 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) - } - - /** - * Returns an immutable instance of [OutputText]. - * - * Further updates to this [Builder] will not mutate the returned - * instance. - * - * The following fields are required: - * ```java - * .text() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): OutputText = - OutputText( - checkRequired("text", text), - type, - additionalProperties.toMutableMap(), - ) - } + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, additionalProperties) } + /* spotless:on */ - private var validated: Boolean = false - - fun validate(): OutputText = apply { - if (validated) { - return@apply - } - - text() - _type().let { - if (it != JsonValue.from("output_text")) { - throw OpenAIInvalidDataException( - "'type' is invalid, received $it" - ) - } - } - validated = true - } + override fun hashCode(): Int = hashCode - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this - * object recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (if (text.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("output_text")) 1 else 0 } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is OutputText && text == other.text && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ - } + override fun toString() = + "EvalGraderPython{name=$name, source=$source, type=$type, imageTag=$imageTag, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" + } - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(text, type, additionalProperties) } - /* spotless:on */ + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + class EvalGraderScoreModel + private constructor( + private val input: JsonField>, + private val model: JsonField, + private val name: JsonField, + private val type: JsonValue, + private val range: JsonField>, + private val samplingParams: JsonValue, + private val passThreshold: JsonField, + private val additionalProperties: MutableMap, + ) { - override fun hashCode(): Int = hashCode + @JsonCreator + private constructor( + @JsonProperty("input") + @ExcludeMissing + input: JsonField> = JsonMissing.of(), + @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("range") + @ExcludeMissing + range: JsonField> = JsonMissing.of(), + @JsonProperty("sampling_params") + @ExcludeMissing + samplingParams: JsonValue = JsonMissing.of(), + @JsonProperty("pass_threshold") + @ExcludeMissing + passThreshold: JsonField = JsonMissing.of(), + ) : this(input, model, name, type, range, samplingParams, passThreshold, mutableMapOf()) + + fun toScoreModelGrader(): ScoreModelGrader = + ScoreModelGrader.builder() + .input(input) + .model(model) + .name(name) + .type(type) + .range(range) + .samplingParams(samplingParams) + .build() - override fun toString() = - "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" - } - } + /** + * The input text. This may include template strings. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun input(): List = input.getRequired("input") - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - */ - class Role @JsonCreator private constructor(private val value: JsonField) : - Enum { + /** + * The model to use for the evaluation. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun model(): String = model.getRequired("model") - /** - * 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 + /** + * The name of the grader. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun name(): String = name.getRequired("name") - companion object { + /** + * The object type, which is always `score_model`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("score_model") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - @JvmField val USER = of("user") + /** + * The range of the score. Defaults to `[0, 1]`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun range(): Optional> = range.getOptional("range") - @JvmField val ASSISTANT = of("assistant") + /** The sampling parameters for the model. */ + @JsonProperty("sampling_params") + @ExcludeMissing + fun _samplingParams(): JsonValue = samplingParams - @JvmField val SYSTEM = of("system") + /** + * The threshold for the score. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") - @JvmField val DEVELOPER = of("developer") + /** + * Returns the raw JSON value of [input]. + * + * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("input") + @ExcludeMissing + fun _input(): JsonField> = input - @JvmStatic fun of(value: String) = Role(JsonField.of(value)) - } + /** + * Returns the raw JSON value of [model]. + * + * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model - /** An enum containing [Role]'s known values. */ - enum class Known { - USER, - ASSISTANT, - SYSTEM, - DEVELOPER, - } + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name - /** - * An enum containing [Role]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Role] 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 { - USER, - ASSISTANT, - SYSTEM, - DEVELOPER, - /** - * An enum member indicating that [Role] was instantiated with an unknown - * value. - */ - _UNKNOWN, - } + /** + * Returns the raw JSON value of [range]. + * + * Unlike [range], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("range") @ExcludeMissing fun _range(): JsonField> = range - /** - * 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) { - USER -> Value.USER - ASSISTANT -> Value.ASSISTANT - SYSTEM -> Value.SYSTEM - DEVELOPER -> Value.DEVELOPER - else -> Value._UNKNOWN - } + /** + * Returns the raw JSON value of [passThreshold]. + * + * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pass_threshold") + @ExcludeMissing + fun _passThreshold(): JsonField = passThreshold - /** - * 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) { - USER -> Known.USER - ASSISTANT -> Known.ASSISTANT - SYSTEM -> Known.SYSTEM - DEVELOPER -> Known.DEVELOPER - else -> throw OpenAIInvalidDataException("Unknown Role: $value") - } + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, 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") - } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - private var validated: Boolean = false + fun toBuilder() = Builder().from(this) - fun validate(): Role = apply { - if (validated) { - return@apply - } + companion object { - known() - validated = true - } + /** + * Returns a mutable builder for constructing an instance of [EvalGraderScoreModel]. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + */ + @JvmStatic fun builder() = Builder() + } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } + /** A builder for [EvalGraderScoreModel]. */ + class Builder internal constructor() { - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + private var input: JsonField>? = null + private var model: JsonField? = null + private var name: JsonField? = null + private var type: JsonValue = JsonValue.from("score_model") + private var range: JsonField>? = null + private var samplingParams: JsonValue = JsonMissing.of() + private var passThreshold: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + @JvmSynthetic + internal fun from(evalGraderScoreModel: EvalGraderScoreModel) = apply { + input = evalGraderScoreModel.input.map { it.toMutableList() } + model = evalGraderScoreModel.model + name = evalGraderScoreModel.name + type = evalGraderScoreModel.type + range = evalGraderScoreModel.range.map { it.toMutableList() } + samplingParams = evalGraderScoreModel.samplingParams + passThreshold = evalGraderScoreModel.passThreshold + additionalProperties = evalGraderScoreModel.additionalProperties.toMutableMap() + } - return /* spotless:off */ other is Role && value == other.value /* spotless:on */ - } + /** The input text. This may include template strings. */ + fun input(input: List) = input(JsonField.of(input)) - override fun hashCode() = value.hashCode() + /** + * Sets [Builder.input] to an arbitrary JSON value. + * + * You should usually call [Builder.input] with a well-typed + * `List` value instead. This method is primarily for + * setting the field to an undocumented or not yet supported value. + */ + fun input(input: JsonField>) = apply { + this.input = input.map { it.toMutableList() } + } - override fun toString() = value.toString() + /** + * Adds a single [ScoreModelGrader.Input] to [Builder.input]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addInput(input: ScoreModelGrader.Input) = apply { + this.input = + (this.input ?: JsonField.of(mutableListOf())).also { + checkKnown("input", it).add(input) + } } - /** The type of the message input. Always `message`. */ - class Type @JsonCreator private constructor(private val value: JsonField) : - Enum { + /** The model to use for the evaluation. */ + fun model(model: String) = model(JsonField.of(model)) - /** - * 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 + /** + * Sets [Builder.model] to an arbitrary JSON value. + * + * You should usually call [Builder.model] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun model(model: JsonField) = apply { this.model = model } - companion object { + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) - @JvmField val MESSAGE = of("message") + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun name(name: JsonField) = apply { this.name = name } - @JvmStatic fun of(value: String) = Type(JsonField.of(value)) - } + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("score_model") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } - /** An enum containing [Type]'s known values. */ - enum class Known { - MESSAGE - } + /** The range of the score. Defaults to `[0, 1]`. */ + fun range(range: List) = range(JsonField.of(range)) - /** - * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Type] 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 { - MESSAGE, - /** - * An enum member indicating that [Type] was instantiated with an unknown - * value. - */ - _UNKNOWN, - } + /** + * Sets [Builder.range] to an arbitrary JSON value. + * + * You should usually call [Builder.range] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun range(range: JsonField>) = apply { + this.range = range.map { it.toMutableList() } + } - /** - * 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) { - MESSAGE -> Value.MESSAGE - else -> Value._UNKNOWN + /** + * Adds a single [Double] to [Builder.range]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addRange(range: Double) = apply { + this.range = + (this.range ?: JsonField.of(mutableListOf())).also { + checkKnown("range", it).add(range) } + } - /** - * 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) { - MESSAGE -> Known.MESSAGE - else -> throw OpenAIInvalidDataException("Unknown Type: $value") - } + /** The sampling parameters for the model. */ + fun samplingParams(samplingParams: JsonValue) = apply { + this.samplingParams = samplingParams + } - /** - * 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") - } + /** The threshold for the score. */ + fun passThreshold(passThreshold: Double) = + passThreshold(JsonField.of(passThreshold)) + + /** + * Sets [Builder.passThreshold] to an arbitrary JSON value. + * + * You should usually call [Builder.passThreshold] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun passThreshold(passThreshold: JsonField) = apply { + this.passThreshold = passThreshold + } - private var validated: Boolean = false + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - fun validate(): Type = apply { - if (validated) { - return@apply - } + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - known() - validated = true + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - return /* spotless:off */ other is Type && value == other.value /* spotless:on */ - } + /** + * Returns an immutable instance of [EvalGraderScoreModel]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): EvalGraderScoreModel = + EvalGraderScoreModel( + checkRequired("input", input).map { it.toImmutable() }, + checkRequired("model", model), + checkRequired("name", name), + type, + (range ?: JsonMissing.of()).map { it.toImmutable() }, + samplingParams, + passThreshold, + additionalProperties.toMutableMap(), + ) + } - override fun hashCode() = value.hashCode() + private var validated: Boolean = false - override fun toString() = value.toString() + fun validate(): EvalGraderScoreModel = apply { + if (validated) { + return@apply } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + input().forEach { it.validate() } + model() + name() + _type().let { + if (it != JsonValue.from("score_model")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") } - - return /* spotless:off */ other is Input && content == other.content && role == other.role && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ } + range() + passThreshold() + validated = true + } - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(content, role, type, additionalProperties) } - /* spotless:on */ - - override fun hashCode(): Int = hashCode + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } - override fun toString() = - "Input{content=$content, role=$role, type=$type, additionalProperties=$additionalProperties}" - } + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (input.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (model.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("score_model")) 1 else 0 } + + (range.asKnown().getOrNull()?.size ?: 0) + + (if (passThreshold.asKnown().isPresent) 1 else 0) override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is ScoreModel && input == other.input && model == other.model && name == other.name && type == other.type && passThreshold == other.passThreshold && range == other.range && samplingParams == other.samplingParams && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is EvalGraderScoreModel && input == other.input && model == other.model && name == other.name && type == other.type && range == other.range && samplingParams == other.samplingParams && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(input, model, name, type, passThreshold, range, samplingParams, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(input, model, name, type, range, samplingParams, passThreshold, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "ScoreModel{input=$input, model=$model, name=$name, type=$type, passThreshold=$passThreshold, range=$range, samplingParams=$samplingParams, additionalProperties=$additionalProperties}" + "EvalGraderScoreModel{input=$input, model=$model, name=$name, type=$type, range=$range, samplingParams=$samplingParams, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/CreateEvalCompletionsRunDataSource.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/CreateEvalCompletionsRunDataSource.kt index 643d4983..5e7249e9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/CreateEvalCompletionsRunDataSource.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/CreateEvalCompletionsRunDataSource.kt @@ -58,7 +58,7 @@ private constructor( ) : this(source, type, inputMessages, model, samplingParams, mutableMapOf()) /** - * A StoredCompletionsRunDataSource configuration describing a set of filters + * Determines what populates the `item` namespace in this run's data source. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). @@ -74,6 +74,10 @@ private constructor( fun type(): Type = type.getRequired("type") /** + * Used when sampling from a model. Dictates the structure of the messages passed into the + * model. Can either be a reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a + * template with variable references to the `item` namespace. + * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ @@ -181,7 +185,7 @@ private constructor( createEvalCompletionsRunDataSource.additionalProperties.toMutableMap() } - /** A StoredCompletionsRunDataSource configuration describing a set of filters */ + /** Determines what populates the `item` namespace in this run's data source. */ fun source(source: Source) = source(JsonField.of(source)) /** @@ -234,6 +238,11 @@ private constructor( */ fun type(type: JsonField) = apply { this.type = type } + /** + * Used when sampling from a model. Dictates the structure of the messages passed into the + * model. Can either be a reference to a prebuilt trajectory (ie, `item.input_trajectory`), + * or a template with variable references to the `item` namespace. + */ fun inputMessages(inputMessages: InputMessages) = inputMessages(JsonField.of(inputMessages)) /** @@ -385,7 +394,7 @@ private constructor( (if (model.asKnown().isPresent) 1 else 0) + (samplingParams.asKnown().getOrNull()?.validity() ?: 0) - /** A StoredCompletionsRunDataSource configuration describing a set of filters */ + /** Determines what populates the `item` namespace in this run's data source. */ @JsonDeserialize(using = Source.Deserializer::class) @JsonSerialize(using = Source.Serializer::class) class Source @@ -2045,6 +2054,11 @@ private constructor( override fun toString() = value.toString() } + /** + * Used when sampling from a model. Dictates the structure of the messages passed into the + * model. Can either be a reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a + * template with variable references to the `item` namespace. + */ @JsonDeserialize(using = InputMessages.Deserializer::class) @JsonSerialize(using = InputMessages.Serializer::class) class InputMessages @@ -2231,7 +2245,7 @@ private constructor( /** * A list of chat messages forming the prompt or context. May include variable - * references to the "item" namespace, ie {{item.name}}. + * references to the `item` namespace, ie {{item.name}}. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected @@ -2303,7 +2317,7 @@ private constructor( /** * A list of chat messages forming the prompt or context. May include variable - * references to the "item" namespace, ie {{item.name}}. + * references to the `item` namespace, ie {{item.name}}. */ fun template(template: List) = template(JsonField.of(template)) @@ -3719,7 +3733,7 @@ private constructor( ) : this(itemReference, type, mutableMapOf()) /** - * A reference to a variable in the "item" namespace. Ie, "item.name" + * A reference to a variable in the `item` namespace. Ie, "item.input_trajectory" * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected @@ -3789,7 +3803,9 @@ private constructor( additionalProperties = itemReference.additionalProperties.toMutableMap() } - /** A reference to a variable in the "item" namespace. Ie, "item.name" */ + /** + * A reference to a variable in the `item` namespace. Ie, "item.input_trajectory" + */ fun itemReference(itemReference: String) = itemReference(JsonField.of(itemReference)) diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/CreateEvalJsonlRunDataSource.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/CreateEvalJsonlRunDataSource.kt index b5eec8f4..6b108302 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/CreateEvalJsonlRunDataSource.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/CreateEvalJsonlRunDataSource.kt @@ -44,6 +44,8 @@ private constructor( ) : this(source, type, mutableMapOf()) /** + * Determines what populates the `item` namespace in the data source. + * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ @@ -108,6 +110,7 @@ private constructor( additionalProperties = createEvalJsonlRunDataSource.additionalProperties.toMutableMap() } + /** Determines what populates the `item` namespace in the data source. */ fun source(source: Source) = source(JsonField.of(source)) /** @@ -232,6 +235,7 @@ private constructor( (source.asKnown().getOrNull()?.validity() ?: 0) + type.let { if (it == JsonValue.from("jsonl")) 1 else 0 } + /** Determines what populates the `item` namespace in the data source. */ @JsonDeserialize(using = Source.Deserializer::class) @JsonSerialize(using = Source.Serializer::class) class Source diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCancelParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCancelParams.kt index 63c37ab0..05903cbd 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCancelParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCancelParams.kt @@ -10,12 +10,13 @@ import com.openai.core.http.QueryParams import com.openai.core.toImmutable import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Cancel an ongoing evaluation run. */ class RunCancelParams private constructor( private val evalId: String, - private val runId: String, + private val runId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, @@ -23,7 +24,7 @@ private constructor( fun evalId(): String = evalId - fun runId(): String = runId + fun runId(): Optional = Optional.ofNullable(runId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -41,7 +42,6 @@ private constructor( * The following fields are required: * ```java * .evalId() - * .runId() * ``` */ @JvmStatic fun builder() = Builder() @@ -67,7 +67,10 @@ private constructor( fun evalId(evalId: String) = apply { this.evalId = evalId } - fun runId(runId: String) = apply { this.runId = runId } + fun runId(runId: String?) = apply { this.runId = runId } + + /** Alias for calling [Builder.runId] with `runId.orElse(null)`. */ + fun runId(runId: Optional) = runId(runId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -197,7 +200,6 @@ private constructor( * The following fields are required: * ```java * .evalId() - * .runId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -205,7 +207,7 @@ private constructor( fun build(): RunCancelParams = RunCancelParams( checkRequired("evalId", evalId), - checkRequired("runId", runId), + runId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -218,7 +220,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> evalId - 1 -> runId + 1 -> runId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCancelResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCancelResponse.kt index 1832409b..332255d4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCancelResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCancelResponse.kt @@ -484,14 +484,9 @@ private constructor( fun fileIdJsonlDataSource(id: String) = jsonlDataSource(CreateEvalJsonlRunDataSource.Source.FileId.builder().id(id).build()) - /** - * Alias for calling [dataSource] with - * `DataSource.ofCreateEvalCompletionsRunDataSource(createEvalCompletionsRunDataSource)`. - */ - fun dataSource(createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource) = - dataSource( - DataSource.ofCreateEvalCompletionsRunDataSource(createEvalCompletionsRunDataSource) - ) + /** Alias for calling [dataSource] with `DataSource.ofCompletions(completions)`. */ + fun dataSource(completions: CreateEvalCompletionsRunDataSource) = + dataSource(DataSource.ofCompletions(completions)) /** * Alias for calling [dataSource] with the following: @@ -502,9 +497,7 @@ private constructor( * .build() * ``` */ - fun createEvalCompletionsRunDataSourceDataSource( - source: CreateEvalCompletionsRunDataSource.Source - ) = + fun completionsDataSource(source: CreateEvalCompletionsRunDataSource.Source) = dataSource( CreateEvalCompletionsRunDataSource.builder() .type(CreateEvalCompletionsRunDataSource.Type.COMPLETIONS) @@ -513,129 +506,125 @@ private constructor( ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with + * Alias for calling [completionsDataSource] with * `CreateEvalCompletionsRunDataSource.Source.ofFileContent(fileContent)`. */ - fun createEvalCompletionsRunDataSourceDataSource( + fun completionsDataSource( fileContent: CreateEvalCompletionsRunDataSource.Source.FileContent ) = - createEvalCompletionsRunDataSourceDataSource( + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.ofFileContent(fileContent) ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with the following: + * Alias for calling [completionsDataSource] with the following: * ```java * CreateEvalCompletionsRunDataSource.Source.FileContent.builder() * .content(content) * .build() * ``` */ - fun fileContentCreateEvalCompletionsRunDataSourceDataSource( + fun fileContentCompletionsDataSource( content: List ) = - createEvalCompletionsRunDataSourceDataSource( + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.FileContent.builder() .content(content) .build() ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with + * Alias for calling [completionsDataSource] with * `CreateEvalCompletionsRunDataSource.Source.ofFileId(fileId)`. */ - fun createEvalCompletionsRunDataSourceDataSource( - fileId: CreateEvalCompletionsRunDataSource.Source.FileId - ) = - createEvalCompletionsRunDataSourceDataSource( - CreateEvalCompletionsRunDataSource.Source.ofFileId(fileId) - ) + fun completionsDataSource(fileId: CreateEvalCompletionsRunDataSource.Source.FileId) = + completionsDataSource(CreateEvalCompletionsRunDataSource.Source.ofFileId(fileId)) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with the following: + * Alias for calling [completionsDataSource] with the following: * ```java * CreateEvalCompletionsRunDataSource.Source.FileId.builder() * .id(id) * .build() * ``` */ - fun fileIdCreateEvalCompletionsRunDataSourceDataSource(id: String) = - createEvalCompletionsRunDataSourceDataSource( + fun fileIdCompletionsDataSource(id: String) = + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.FileId.builder().id(id).build() ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with + * Alias for calling [completionsDataSource] with * `CreateEvalCompletionsRunDataSource.Source.ofStoredCompletions(storedCompletions)`. */ - fun createEvalCompletionsRunDataSourceDataSource( + fun completionsDataSource( storedCompletions: CreateEvalCompletionsRunDataSource.Source.StoredCompletions ) = - createEvalCompletionsRunDataSourceDataSource( + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.ofStoredCompletions(storedCompletions) ) - /** Alias for calling [dataSource] with `DataSource.ofCompletions(completions)`. */ - fun dataSource(completions: DataSource.Completions) = - dataSource(DataSource.ofCompletions(completions)) + /** Alias for calling [dataSource] with `DataSource.ofResponses(responses)`. */ + fun dataSource(responses: DataSource.Responses) = + dataSource(DataSource.ofResponses(responses)) /** * Alias for calling [dataSource] with the following: * ```java - * DataSource.Completions.builder() + * DataSource.Responses.builder() * .source(source) * .build() * ``` */ - fun completionsDataSource(source: DataSource.Completions.Source) = - dataSource(DataSource.Completions.builder().source(source).build()) + fun responsesDataSource(source: DataSource.Responses.Source) = + dataSource(DataSource.Responses.builder().source(source).build()) /** - * Alias for calling [completionsDataSource] with - * `DataSource.Completions.Source.ofFileContent(fileContent)`. + * Alias for calling [responsesDataSource] with + * `DataSource.Responses.Source.ofFileContent(fileContent)`. */ - fun completionsDataSource(fileContent: DataSource.Completions.Source.FileContent) = - completionsDataSource(DataSource.Completions.Source.ofFileContent(fileContent)) + fun responsesDataSource(fileContent: DataSource.Responses.Source.FileContent) = + responsesDataSource(DataSource.Responses.Source.ofFileContent(fileContent)) /** - * Alias for calling [completionsDataSource] with the following: + * Alias for calling [responsesDataSource] with the following: * ```java - * DataSource.Completions.Source.FileContent.builder() + * DataSource.Responses.Source.FileContent.builder() * .content(content) * .build() * ``` */ - fun fileContentCompletionsDataSource( - content: List + fun fileContentResponsesDataSource( + content: List ) = - completionsDataSource( - DataSource.Completions.Source.FileContent.builder().content(content).build() + responsesDataSource( + DataSource.Responses.Source.FileContent.builder().content(content).build() ) /** - * Alias for calling [completionsDataSource] with - * `DataSource.Completions.Source.ofFileId(fileId)`. + * Alias for calling [responsesDataSource] with + * `DataSource.Responses.Source.ofFileId(fileId)`. */ - fun completionsDataSource(fileId: DataSource.Completions.Source.FileId) = - completionsDataSource(DataSource.Completions.Source.ofFileId(fileId)) + fun responsesDataSource(fileId: DataSource.Responses.Source.FileId) = + responsesDataSource(DataSource.Responses.Source.ofFileId(fileId)) /** - * Alias for calling [completionsDataSource] with the following: + * Alias for calling [responsesDataSource] with the following: * ```java - * DataSource.Completions.Source.FileId.builder() + * DataSource.Responses.Source.FileId.builder() * .id(id) * .build() * ``` */ - fun fileIdCompletionsDataSource(id: String) = - completionsDataSource(DataSource.Completions.Source.FileId.builder().id(id).build()) + fun fileIdResponsesDataSource(id: String) = + responsesDataSource(DataSource.Responses.Source.FileId.builder().id(id).build()) /** - * Alias for calling [completionsDataSource] with - * `DataSource.Completions.Source.ofResponses(responses)`. + * Alias for calling [responsesDataSource] with + * `DataSource.Responses.Source.ofResponses(responses)`. */ - fun completionsDataSource(responses: DataSource.Completions.Source.Responses) = - completionsDataSource(DataSource.Completions.Source.ofResponses(responses)) + fun responsesDataSource(responses: DataSource.Responses.Source.InnerResponses) = + responsesDataSource(DataSource.Responses.Source.ofResponses(responses)) /** An object representing an error response from the Eval API. */ fun error(error: EvalApiError) = error(JsonField.of(error)) @@ -942,8 +931,8 @@ private constructor( class DataSource private constructor( private val jsonl: CreateEvalJsonlRunDataSource? = null, - private val createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource? = null, - private val completions: Completions? = null, + private val completions: CreateEvalCompletionsRunDataSource? = null, + private val responses: Responses? = null, private val _json: JsonValue? = null, ) { @@ -951,39 +940,35 @@ private constructor( fun jsonl(): Optional = Optional.ofNullable(jsonl) /** A CompletionsRunDataSource object describing a model sampling configuration. */ - fun createEvalCompletionsRunDataSource(): Optional = - Optional.ofNullable(createEvalCompletionsRunDataSource) + fun completions(): Optional = + Optional.ofNullable(completions) /** A ResponsesRunDataSource object describing a model sampling configuration. */ - fun completions(): Optional = Optional.ofNullable(completions) + fun responses(): Optional = Optional.ofNullable(responses) fun isJsonl(): Boolean = jsonl != null - fun isCreateEvalCompletionsRunDataSource(): Boolean = - createEvalCompletionsRunDataSource != null - fun isCompletions(): Boolean = completions != null + fun isResponses(): Boolean = responses != null + /** A JsonlRunDataSource object with that specifies a JSONL file that matches the eval */ fun asJsonl(): CreateEvalJsonlRunDataSource = jsonl.getOrThrow("jsonl") /** A CompletionsRunDataSource object describing a model sampling configuration. */ - fun asCreateEvalCompletionsRunDataSource(): CreateEvalCompletionsRunDataSource = - createEvalCompletionsRunDataSource.getOrThrow("createEvalCompletionsRunDataSource") + fun asCompletions(): CreateEvalCompletionsRunDataSource = + completions.getOrThrow("completions") /** A ResponsesRunDataSource object describing a model sampling configuration. */ - fun asCompletions(): Completions = completions.getOrThrow("completions") + fun asResponses(): Responses = responses.getOrThrow("responses") fun _json(): Optional = Optional.ofNullable(_json) fun accept(visitor: Visitor): T = when { jsonl != null -> visitor.visitJsonl(jsonl) - createEvalCompletionsRunDataSource != null -> - visitor.visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource - ) completions != null -> visitor.visitCompletions(completions) + responses != null -> visitor.visitResponses(responses) else -> visitor.unknown(_json) } @@ -1000,14 +985,12 @@ private constructor( jsonl.validate() } - override fun visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ) { - createEvalCompletionsRunDataSource.validate() + override fun visitCompletions(completions: CreateEvalCompletionsRunDataSource) { + completions.validate() } - override fun visitCompletions(completions: Completions) { - completions.validate() + override fun visitResponses(responses: Responses) { + responses.validate() } } ) @@ -1034,11 +1017,10 @@ private constructor( object : Visitor { override fun visitJsonl(jsonl: CreateEvalJsonlRunDataSource) = jsonl.validity() - override fun visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ) = createEvalCompletionsRunDataSource.validity() + override fun visitCompletions(completions: CreateEvalCompletionsRunDataSource) = + completions.validity() - override fun visitCompletions(completions: Completions) = completions.validity() + override fun visitResponses(responses: Responses) = responses.validity() override fun unknown(json: JsonValue?) = 0 } @@ -1049,17 +1031,16 @@ private constructor( return true } - return /* spotless:off */ other is DataSource && jsonl == other.jsonl && createEvalCompletionsRunDataSource == other.createEvalCompletionsRunDataSource && completions == other.completions /* spotless:on */ + return /* spotless:off */ other is DataSource && jsonl == other.jsonl && completions == other.completions && responses == other.responses /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(jsonl, createEvalCompletionsRunDataSource, completions) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(jsonl, completions, responses) /* spotless:on */ override fun toString(): String = when { jsonl != null -> "DataSource{jsonl=$jsonl}" - createEvalCompletionsRunDataSource != null -> - "DataSource{createEvalCompletionsRunDataSource=$createEvalCompletionsRunDataSource}" completions != null -> "DataSource{completions=$completions}" + responses != null -> "DataSource{responses=$responses}" _json != null -> "DataSource{_unknown=$_json}" else -> throw IllegalStateException("Invalid DataSource") } @@ -1073,13 +1054,11 @@ private constructor( /** A CompletionsRunDataSource object describing a model sampling configuration. */ @JvmStatic - fun ofCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ) = DataSource(createEvalCompletionsRunDataSource = createEvalCompletionsRunDataSource) + fun ofCompletions(completions: CreateEvalCompletionsRunDataSource) = + DataSource(completions = completions) /** A ResponsesRunDataSource object describing a model sampling configuration. */ - @JvmStatic - fun ofCompletions(completions: Completions) = DataSource(completions = completions) + @JvmStatic fun ofResponses(responses: Responses) = DataSource(responses = responses) } /** @@ -1093,12 +1072,10 @@ private constructor( fun visitJsonl(jsonl: CreateEvalJsonlRunDataSource): T /** A CompletionsRunDataSource object describing a model sampling configuration. */ - fun visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ): T + fun visitCompletions(completions: CreateEvalCompletionsRunDataSource): T /** A ResponsesRunDataSource object describing a model sampling configuration. */ - fun visitCompletions(completions: Completions): T + fun visitResponses(responses: Responses): T /** * Maps an unknown variant of [DataSource] to a value of type [T]. @@ -1128,36 +1105,17 @@ private constructor( ?: DataSource(_json = json) } "completions" -> { - val bestMatches = - sequenceOf( - tryDeserialize( - node, - jacksonTypeRef(), - ) - ?.let { - DataSource( - createEvalCompletionsRunDataSource = it, - _json = json, - ) - }, - tryDeserialize(node, jacksonTypeRef())?.let { - DataSource(completions = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing from - // boolean). - 0 -> DataSource(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then use - // the first completely valid match, or simply the first match if none - // are completely valid. - else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { DataSource(completions = it, _json = json) } + ?: DataSource(_json = json) + } + "responses" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DataSource(responses = it, _json = json) + } ?: DataSource(_json = json) } } @@ -1174,9 +1132,8 @@ private constructor( ) { when { value.jsonl != null -> generator.writeObject(value.jsonl) - value.createEvalCompletionsRunDataSource != null -> - generator.writeObject(value.createEvalCompletionsRunDataSource) value.completions != null -> generator.writeObject(value.completions) + value.responses != null -> generator.writeObject(value.responses) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid DataSource") } @@ -1184,7 +1141,7 @@ private constructor( } /** A ResponsesRunDataSource object describing a model sampling configuration. */ - class Completions + class Responses private constructor( private val source: JsonField, private val type: JsonValue, @@ -1210,7 +1167,7 @@ private constructor( ) : this(source, type, inputMessages, model, samplingParams, mutableMapOf()) /** - * A EvalResponsesSource object describing a run data source configuration. + * Determines what populates the `item` namespace in this run's data source. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected @@ -1219,11 +1176,11 @@ private constructor( fun source(): Source = source.getRequired("source") /** - * The type of run data source. Always `completions`. + * The type of run data source. Always `responses`. * * Expected to always return the following: * ```java - * JsonValue.from("completions") + * JsonValue.from("responses") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1232,6 +1189,11 @@ private constructor( @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** + * Used when sampling from a model. Dictates the structure of the messages passed into + * the model. Can either be a reference to a prebuilt trajectory (ie, + * `item.input_trajectory`), or a template with variable references to the `item` + * namespace. + * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ @@ -1302,7 +1264,7 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [Completions]. + * Returns a mutable builder for constructing an instance of [Responses]. * * The following fields are required: * ```java @@ -1312,27 +1274,27 @@ private constructor( @JvmStatic fun builder() = Builder() } - /** A builder for [Completions]. */ + /** A builder for [Responses]. */ class Builder internal constructor() { private var source: JsonField? = null - private var type: JsonValue = JsonValue.from("completions") + private var type: JsonValue = JsonValue.from("responses") private var inputMessages: JsonField = JsonMissing.of() private var model: JsonField = JsonMissing.of() private var samplingParams: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(completions: Completions) = apply { - source = completions.source - type = completions.type - inputMessages = completions.inputMessages - model = completions.model - samplingParams = completions.samplingParams - additionalProperties = completions.additionalProperties.toMutableMap() + internal fun from(responses: Responses) = apply { + source = responses.source + type = responses.type + inputMessages = responses.inputMessages + model = responses.model + samplingParams = responses.samplingParams + additionalProperties = responses.additionalProperties.toMutableMap() } - /** A EvalResponsesSource object describing a run data source configuration. */ + /** Determines what populates the `item` namespace in this run's data source. */ fun source(source: Source) = source(JsonField.of(source)) /** @@ -1373,7 +1335,7 @@ private constructor( fun fileIdSource(id: String) = source(Source.FileId.builder().id(id).build()) /** Alias for calling [source] with `Source.ofResponses(responses)`. */ - fun source(responses: Source.Responses) = source(Source.ofResponses(responses)) + fun source(responses: Source.InnerResponses) = source(Source.ofResponses(responses)) /** * Sets the field to an arbitrary JSON value. @@ -1381,7 +1343,7 @@ private constructor( * It is usually unnecessary to call this method because the field defaults to the * following: * ```java - * JsonValue.from("completions") + * JsonValue.from("responses") * ``` * * This method is primarily for setting the field to an undocumented or not yet @@ -1389,6 +1351,12 @@ private constructor( */ fun type(type: JsonValue) = apply { this.type = type } + /** + * Used when sampling from a model. Dictates the structure of the messages passed + * into the model. Can either be a reference to a prebuilt trajectory (ie, + * `item.input_trajectory`), or a template with variable references to the `item` + * namespace. + */ fun inputMessages(inputMessages: InputMessages) = inputMessages(JsonField.of(inputMessages)) @@ -1487,7 +1455,7 @@ private constructor( } /** - * Returns an immutable instance of [Completions]. + * Returns an immutable instance of [Responses]. * * Further updates to this [Builder] will not mutate the returned instance. * @@ -1498,8 +1466,8 @@ private constructor( * * @throws IllegalStateException if any required field is unset. */ - fun build(): Completions = - Completions( + fun build(): Responses = + Responses( checkRequired("source", source), type, inputMessages, @@ -1511,14 +1479,14 @@ private constructor( private var validated: Boolean = false - fun validate(): Completions = apply { + fun validate(): Responses = apply { if (validated) { return@apply } source().validate() _type().let { - if (it != JsonValue.from("completions")) { + if (it != JsonValue.from("responses")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") } } @@ -1545,19 +1513,19 @@ private constructor( @JvmSynthetic internal fun validity(): Int = (source.asKnown().getOrNull()?.validity() ?: 0) + - type.let { if (it == JsonValue.from("completions")) 1 else 0 } + + type.let { if (it == JsonValue.from("responses")) 1 else 0 } + (inputMessages.asKnown().getOrNull()?.validity() ?: 0) + (if (model.asKnown().isPresent) 1 else 0) + (samplingParams.asKnown().getOrNull()?.validity() ?: 0) - /** A EvalResponsesSource object describing a run data source configuration. */ + /** Determines what populates the `item` namespace in this run's data source. */ @JsonDeserialize(using = Source.Deserializer::class) @JsonSerialize(using = Source.Serializer::class) class Source private constructor( private val fileContent: FileContent? = null, private val fileId: FileId? = null, - private val responses: Responses? = null, + private val responses: InnerResponses? = null, private val _json: JsonValue? = null, ) { @@ -1566,7 +1534,7 @@ private constructor( fun fileId(): Optional = Optional.ofNullable(fileId) /** A EvalResponsesSource object describing a run data source configuration. */ - fun responses(): Optional = Optional.ofNullable(responses) + fun responses(): Optional = Optional.ofNullable(responses) fun isFileContent(): Boolean = fileContent != null @@ -1579,7 +1547,7 @@ private constructor( fun asFileId(): FileId = fileId.getOrThrow("fileId") /** A EvalResponsesSource object describing a run data source configuration. */ - fun asResponses(): Responses = responses.getOrThrow("responses") + fun asResponses(): InnerResponses = responses.getOrThrow("responses") fun _json(): Optional = Optional.ofNullable(_json) @@ -1608,7 +1576,7 @@ private constructor( fileId.validate() } - override fun visitResponses(responses: Responses) { + override fun visitResponses(responses: InnerResponses) { responses.validate() } } @@ -1639,7 +1607,8 @@ private constructor( override fun visitFileId(fileId: FileId) = fileId.validity() - override fun visitResponses(responses: Responses) = responses.validity() + override fun visitResponses(responses: InnerResponses) = + responses.validity() override fun unknown(json: JsonValue?) = 0 } @@ -1672,7 +1641,8 @@ private constructor( @JvmStatic fun ofFileId(fileId: FileId) = Source(fileId = fileId) /** A EvalResponsesSource object describing a run data source configuration. */ - @JvmStatic fun ofResponses(responses: Responses) = Source(responses = responses) + @JvmStatic + fun ofResponses(responses: InnerResponses) = Source(responses = responses) } /** @@ -1686,7 +1656,7 @@ private constructor( fun visitFileId(fileId: FileId): T /** A EvalResponsesSource object describing a run data source configuration. */ - fun visitResponses(responses: Responses): T + fun visitResponses(responses: InnerResponses): T /** * Maps an unknown variant of [Source] to a value of type [T]. @@ -1721,7 +1691,7 @@ private constructor( } ?: Source(_json = json) } "responses" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { + return tryDeserialize(node, jacksonTypeRef())?.let { Source(responses = it, _json = json) } ?: Source(_json = json) } @@ -2608,18 +2578,17 @@ private constructor( } /** A EvalResponsesSource object describing a run data source configuration. */ - class Responses + class InnerResponses private constructor( private val type: JsonValue, - private val allowParallelToolCalls: JsonField, private val createdAfter: JsonField, private val createdBefore: JsonField, - private val hasToolCalls: JsonField, private val instructionsSearch: JsonField, private val metadata: JsonValue, private val model: JsonField, private val reasoningEffort: JsonField, private val temperature: JsonField, + private val tools: JsonField>, private val topP: JsonField, private val users: JsonField>, private val additionalProperties: MutableMap, @@ -2628,18 +2597,12 @@ private constructor( @JsonCreator private constructor( @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - @JsonProperty("allow_parallel_tool_calls") - @ExcludeMissing - allowParallelToolCalls: JsonField = JsonMissing.of(), @JsonProperty("created_after") @ExcludeMissing createdAfter: JsonField = JsonMissing.of(), @JsonProperty("created_before") @ExcludeMissing createdBefore: JsonField = JsonMissing.of(), - @JsonProperty("has_tool_calls") - @ExcludeMissing - hasToolCalls: JsonField = JsonMissing.of(), @JsonProperty("instructions_search") @ExcludeMissing instructionsSearch: JsonField = JsonMissing.of(), @@ -2655,6 +2618,9 @@ private constructor( @JsonProperty("temperature") @ExcludeMissing temperature: JsonField = JsonMissing.of(), + @JsonProperty("tools") + @ExcludeMissing + tools: JsonField> = JsonMissing.of(), @JsonProperty("top_p") @ExcludeMissing topP: JsonField = JsonMissing.of(), @@ -2663,15 +2629,14 @@ private constructor( users: JsonField> = JsonMissing.of(), ) : this( type, - allowParallelToolCalls, createdAfter, createdBefore, - hasToolCalls, instructionsSearch, metadata, model, reasoningEffort, temperature, + tools, topP, users, mutableMapOf(), @@ -2690,16 +2655,6 @@ private constructor( */ @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - /** - * Whether to allow parallel tool calls. This is a query parameter used to - * select responses. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). - */ - fun allowParallelToolCalls(): Optional = - allowParallelToolCalls.getOptional("allow_parallel_tool_calls") - /** * Only include items created after this timestamp (inclusive). This is a query * parameter used to select responses. @@ -2720,18 +2675,8 @@ private constructor( createdBefore.getOptional("created_before") /** - * Whether the response has tool calls. This is a query parameter used to select - * responses. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). - */ - fun hasToolCalls(): Optional = - hasToolCalls.getOptional("has_tool_calls") - - /** - * Optional search string for instructions. This is a query parameter used to - * select responses. + * Optional string to search the 'instructions' field. This is a query parameter + * used to select responses. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * (e.g. if the server responded with an unexpected value). @@ -2772,6 +2717,14 @@ private constructor( */ fun temperature(): Optional = temperature.getOptional("temperature") + /** + * List of tool names. This is a query parameter used to select responses. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun tools(): Optional> = tools.getOptional("tools") + /** * Nucleus sampling parameter. This is a query parameter used to select * responses. @@ -2789,16 +2742,6 @@ private constructor( */ fun users(): Optional> = users.getOptional("users") - /** - * Returns the raw JSON value of [allowParallelToolCalls]. - * - * Unlike [allowParallelToolCalls], this method doesn't throw if the JSON field - * has an unexpected type. - */ - @JsonProperty("allow_parallel_tool_calls") - @ExcludeMissing - fun _allowParallelToolCalls(): JsonField = allowParallelToolCalls - /** * Returns the raw JSON value of [createdAfter]. * @@ -2819,16 +2762,6 @@ private constructor( @ExcludeMissing fun _createdBefore(): JsonField = createdBefore - /** - * Returns the raw JSON value of [hasToolCalls]. - * - * Unlike [hasToolCalls], this method doesn't throw if the JSON field has an - * unexpected type. - */ - @JsonProperty("has_tool_calls") - @ExcludeMissing - fun _hasToolCalls(): JsonField = hasToolCalls - /** * Returns the raw JSON value of [instructionsSearch]. * @@ -2867,6 +2800,16 @@ private constructor( @ExcludeMissing fun _temperature(): JsonField = temperature + /** + * Returns the raw JSON value of [tools]. + * + * Unlike [tools], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("tools") + @ExcludeMissing + fun _tools(): JsonField> = tools + /** * Returns the raw JSON value of [topP]. * @@ -2900,44 +2843,44 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [Responses]. + * Returns a mutable builder for constructing an instance of + * [InnerResponses]. */ @JvmStatic fun builder() = Builder() } - /** A builder for [Responses]. */ + /** A builder for [InnerResponses]. */ class Builder internal constructor() { private var type: JsonValue = JsonValue.from("responses") - private var allowParallelToolCalls: JsonField = JsonMissing.of() private var createdAfter: JsonField = JsonMissing.of() private var createdBefore: JsonField = JsonMissing.of() - private var hasToolCalls: JsonField = JsonMissing.of() private var instructionsSearch: JsonField = JsonMissing.of() private var metadata: JsonValue = JsonMissing.of() private var model: JsonField = JsonMissing.of() private var reasoningEffort: JsonField = JsonMissing.of() private var temperature: JsonField = JsonMissing.of() + private var tools: JsonField>? = null private var topP: JsonField = JsonMissing.of() private var users: JsonField>? = null private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(responses: Responses) = apply { - type = responses.type - allowParallelToolCalls = responses.allowParallelToolCalls - createdAfter = responses.createdAfter - createdBefore = responses.createdBefore - hasToolCalls = responses.hasToolCalls - instructionsSearch = responses.instructionsSearch - metadata = responses.metadata - model = responses.model - reasoningEffort = responses.reasoningEffort - temperature = responses.temperature - topP = responses.topP - users = responses.users.map { it.toMutableList() } - additionalProperties = responses.additionalProperties.toMutableMap() + internal fun from(innerResponses: InnerResponses) = apply { + type = innerResponses.type + createdAfter = innerResponses.createdAfter + createdBefore = innerResponses.createdBefore + instructionsSearch = innerResponses.instructionsSearch + metadata = innerResponses.metadata + model = innerResponses.model + reasoningEffort = innerResponses.reasoningEffort + temperature = innerResponses.temperature + tools = innerResponses.tools.map { it.toMutableList() } + topP = innerResponses.topP + users = innerResponses.users.map { it.toMutableList() } + additionalProperties = + innerResponses.additionalProperties.toMutableMap() } /** @@ -2954,40 +2897,6 @@ private constructor( */ fun type(type: JsonValue) = apply { this.type = type } - /** - * Whether to allow parallel tool calls. This is a query parameter used to - * select responses. - */ - fun allowParallelToolCalls(allowParallelToolCalls: Boolean?) = - allowParallelToolCalls(JsonField.ofNullable(allowParallelToolCalls)) - - /** - * Alias for [Builder.allowParallelToolCalls]. - * - * This unboxed primitive overload exists for backwards compatibility. - */ - fun allowParallelToolCalls(allowParallelToolCalls: Boolean) = - allowParallelToolCalls(allowParallelToolCalls as Boolean?) - - /** - * Alias for calling [Builder.allowParallelToolCalls] with - * `allowParallelToolCalls.orElse(null)`. - */ - fun allowParallelToolCalls(allowParallelToolCalls: Optional) = - allowParallelToolCalls(allowParallelToolCalls.getOrNull()) - - /** - * Sets [Builder.allowParallelToolCalls] to an arbitrary JSON value. - * - * You should usually call [Builder.allowParallelToolCalls] with a - * well-typed [Boolean] value instead. This method is primarily for setting - * the field to an undocumented or not yet supported value. - */ - fun allowParallelToolCalls(allowParallelToolCalls: JsonField) = - apply { - this.allowParallelToolCalls = allowParallelToolCalls - } - /** * Only include items created after this timestamp (inclusive). This is a * query parameter used to select responses. @@ -3054,41 +2963,8 @@ private constructor( } /** - * Whether the response has tool calls. This is a query parameter used to - * select responses. - */ - fun hasToolCalls(hasToolCalls: Boolean?) = - hasToolCalls(JsonField.ofNullable(hasToolCalls)) - - /** - * Alias for [Builder.hasToolCalls]. - * - * This unboxed primitive overload exists for backwards compatibility. - */ - fun hasToolCalls(hasToolCalls: Boolean) = - hasToolCalls(hasToolCalls as Boolean?) - - /** - * Alias for calling [Builder.hasToolCalls] with - * `hasToolCalls.orElse(null)`. - */ - fun hasToolCalls(hasToolCalls: Optional) = - hasToolCalls(hasToolCalls.getOrNull()) - - /** - * Sets [Builder.hasToolCalls] to an arbitrary JSON value. - * - * You should usually call [Builder.hasToolCalls] with a well-typed - * [Boolean] value instead. This method is primarily for setting the field - * to an undocumented or not yet supported value. - */ - fun hasToolCalls(hasToolCalls: JsonField) = apply { - this.hasToolCalls = hasToolCalls - } - - /** - * Optional search string for instructions. This is a query parameter used - * to select responses. + * Optional string to search the 'instructions' field. This is a query + * parameter used to select responses. */ fun instructionsSearch(instructionsSearch: String?) = instructionsSearch(JsonField.ofNullable(instructionsSearch)) @@ -3190,6 +3066,38 @@ private constructor( this.temperature = temperature } + /** + * List of tool names. This is a query parameter used to select responses. + */ + fun tools(tools: List?) = tools(JsonField.ofNullable(tools)) + + /** Alias for calling [Builder.tools] with `tools.orElse(null)`. */ + fun tools(tools: Optional>) = tools(tools.getOrNull()) + + /** + * Sets [Builder.tools] to an arbitrary JSON value. + * + * You should usually call [Builder.tools] with a well-typed `List` + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun tools(tools: JsonField>) = apply { + this.tools = tools.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [tools]. + * + * @throws IllegalStateException if the field was previously set to a + * non-list. + */ + fun addTool(tool: String) = apply { + tools = + (tools ?: JsonField.of(mutableListOf())).also { + checkKnown("tools", it).add(tool) + } + } + /** * Nucleus sampling parameter. This is a query parameter used to select * responses. @@ -3271,22 +3179,21 @@ private constructor( } /** - * Returns an immutable instance of [Responses]. + * Returns an immutable instance of [InnerResponses]. * * Further updates to this [Builder] will not mutate the returned instance. */ - fun build(): Responses = - Responses( + fun build(): InnerResponses = + InnerResponses( type, - allowParallelToolCalls, createdAfter, createdBefore, - hasToolCalls, instructionsSearch, metadata, model, reasoningEffort, temperature, + (tools ?: JsonMissing.of()).map { it.toImmutable() }, topP, (users ?: JsonMissing.of()).map { it.toImmutable() }, additionalProperties.toMutableMap(), @@ -3295,7 +3202,7 @@ private constructor( private var validated: Boolean = false - fun validate(): Responses = apply { + fun validate(): InnerResponses = apply { if (validated) { return@apply } @@ -3305,14 +3212,13 @@ private constructor( throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - allowParallelToolCalls() createdAfter() createdBefore() - hasToolCalls() instructionsSearch() model() reasoningEffort().ifPresent { it.validate() } temperature() + tools() topP() users() validated = true @@ -3335,14 +3241,13 @@ private constructor( @JvmSynthetic internal fun validity(): Int = type.let { if (it == JsonValue.from("responses")) 1 else 0 } + - (if (allowParallelToolCalls.asKnown().isPresent) 1 else 0) + (if (createdAfter.asKnown().isPresent) 1 else 0) + (if (createdBefore.asKnown().isPresent) 1 else 0) + - (if (hasToolCalls.asKnown().isPresent) 1 else 0) + (if (instructionsSearch.asKnown().isPresent) 1 else 0) + (if (model.asKnown().isPresent) 1 else 0) + (reasoningEffort.asKnown().getOrNull()?.validity() ?: 0) + (if (temperature.asKnown().isPresent) 1 else 0) + + (tools.asKnown().getOrNull()?.size ?: 0) + (if (topP.asKnown().isPresent) 1 else 0) + (users.asKnown().getOrNull()?.size ?: 0) @@ -3351,20 +3256,26 @@ private constructor( return true } - return /* spotless:off */ other is Responses && type == other.type && allowParallelToolCalls == other.allowParallelToolCalls && createdAfter == other.createdAfter && createdBefore == other.createdBefore && hasToolCalls == other.hasToolCalls && instructionsSearch == other.instructionsSearch && metadata == other.metadata && model == other.model && reasoningEffort == other.reasoningEffort && temperature == other.temperature && topP == other.topP && users == other.users && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is InnerResponses && type == other.type && createdAfter == other.createdAfter && createdBefore == other.createdBefore && instructionsSearch == other.instructionsSearch && metadata == other.metadata && model == other.model && reasoningEffort == other.reasoningEffort && temperature == other.temperature && tools == other.tools && topP == other.topP && users == other.users && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(type, allowParallelToolCalls, createdAfter, createdBefore, hasToolCalls, instructionsSearch, metadata, model, reasoningEffort, temperature, topP, users, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(type, createdAfter, createdBefore, instructionsSearch, metadata, model, reasoningEffort, temperature, tools, topP, users, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "Responses{type=$type, allowParallelToolCalls=$allowParallelToolCalls, createdAfter=$createdAfter, createdBefore=$createdBefore, hasToolCalls=$hasToolCalls, instructionsSearch=$instructionsSearch, metadata=$metadata, model=$model, reasoningEffort=$reasoningEffort, temperature=$temperature, topP=$topP, users=$users, additionalProperties=$additionalProperties}" + "InnerResponses{type=$type, createdAfter=$createdAfter, createdBefore=$createdBefore, instructionsSearch=$instructionsSearch, metadata=$metadata, model=$model, reasoningEffort=$reasoningEffort, temperature=$temperature, tools=$tools, topP=$topP, users=$users, additionalProperties=$additionalProperties}" } } + /** + * Used when sampling from a model. Dictates the structure of the messages passed into + * the model. Can either be a reference to a prebuilt trajectory (ie, + * `item.input_trajectory`), or a template with variable references to the `item` + * namespace. + */ @JsonDeserialize(using = InputMessages.Deserializer::class) @JsonSerialize(using = InputMessages.Serializer::class) class InputMessages @@ -3554,7 +3465,7 @@ private constructor( /** * A list of chat messages forming the prompt or context. May include variable - * references to the "item" namespace, ie {{item.name}}. + * references to the `item` namespace, ie {{item.name}}. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * or is unexpectedly missing or null (e.g. if the server responded with an @@ -3627,7 +3538,7 @@ private constructor( /** * A list of chat messages forming the prompt or context. May include - * variable references to the "item" namespace, ie {{item.name}}. + * variable references to the `item` namespace, ie {{item.name}}. */ fun template(template: List) = template(JsonField.of(template)) @@ -5304,7 +5215,7 @@ private constructor( ) : this(itemReference, type, mutableMapOf()) /** - * A reference to a variable in the "item" namespace. Ie, "item.name" + * A reference to a variable in the `item` namespace. Ie, "item.name" * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * or is unexpectedly missing or null (e.g. if the server responded with an @@ -5376,7 +5287,7 @@ private constructor( additionalProperties = itemReference.additionalProperties.toMutableMap() } - /** A reference to a variable in the "item" namespace. Ie, "item.name" */ + /** A reference to a variable in the `item` namespace. Ie, "item.name" */ fun itemReference(itemReference: String) = itemReference(JsonField.of(itemReference)) @@ -5781,7 +5692,7 @@ private constructor( return true } - return /* spotless:off */ other is Completions && source == other.source && type == other.type && inputMessages == other.inputMessages && model == other.model && samplingParams == other.samplingParams && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is Responses && source == other.source && type == other.type && inputMessages == other.inputMessages && model == other.model && samplingParams == other.samplingParams && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ @@ -5791,7 +5702,7 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "Completions{source=$source, type=$type, inputMessages=$inputMessages, model=$model, samplingParams=$samplingParams, additionalProperties=$additionalProperties}" + "Responses{source=$source, type=$type, inputMessages=$inputMessages, model=$model, samplingParams=$samplingParams, additionalProperties=$additionalProperties}" } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateParams.kt index 952e12ba..55b51ecf 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateParams.kt @@ -36,16 +36,20 @@ import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** Create a new evaluation run. This is the endpoint that will kick off grading. */ +/** + * Kicks off a new run for a given evaluation, specifying the data source, and what model + * configuration to use to test. The datasource will be validated against the schema specified in + * the config of the evaluation. + */ class RunCreateParams private constructor( - private val evalId: String, + private val evalId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun evalId(): String = evalId + fun evalId(): Optional = Optional.ofNullable(evalId) /** * Details about the run's data source. @@ -112,7 +116,6 @@ private constructor( * * The following fields are required: * ```java - * .evalId() * .dataSource() * ``` */ @@ -135,7 +138,10 @@ private constructor( additionalQueryParams = runCreateParams.additionalQueryParams.toBuilder() } - fun evalId(evalId: String) = apply { this.evalId = evalId } + fun evalId(evalId: String?) = apply { this.evalId = evalId } + + /** Alias for calling [Builder.evalId] with `evalId.orElse(null)`. */ + fun evalId(evalId: Optional) = evalId(evalId.getOrNull()) /** * Sets the entire request body. @@ -342,7 +348,6 @@ private constructor( * * The following fields are required: * ```java - * .evalId() * .dataSource() * ``` * @@ -350,7 +355,7 @@ private constructor( */ fun build(): RunCreateParams = RunCreateParams( - checkRequired("evalId", evalId), + evalId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -361,7 +366,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> evalId + 0 -> evalId ?: "" else -> "" } @@ -916,7 +921,7 @@ private constructor( ) : this(source, type, inputMessages, model, samplingParams, mutableMapOf()) /** - * A EvalResponsesSource object describing a run data source configuration. + * Determines what populates the `item` namespace in this run's data source. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected @@ -925,7 +930,7 @@ private constructor( fun source(): Source = source.getRequired("source") /** - * The type of run data source. Always `completions`. + * The type of run data source. Always `responses`. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected @@ -934,6 +939,11 @@ private constructor( fun type(): Type = type.getRequired("type") /** + * Used when sampling from a model. Dictates the structure of the messages passed into + * the model. Can either be a reference to a prebuilt trajectory (ie, + * `item.input_trajectory`), or a template with variable references to the `item` + * namespace. + * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ @@ -1046,7 +1056,7 @@ private constructor( createEvalResponsesRunDataSource.additionalProperties.toMutableMap() } - /** A EvalResponsesSource object describing a run data source configuration. */ + /** Determines what populates the `item` namespace in this run's data source. */ fun source(source: Source) = source(JsonField.of(source)) /** @@ -1089,7 +1099,7 @@ private constructor( /** Alias for calling [source] with `Source.ofResponses(responses)`. */ fun source(responses: Source.Responses) = source(Source.ofResponses(responses)) - /** The type of run data source. Always `completions`. */ + /** The type of run data source. Always `responses`. */ fun type(type: Type) = type(JsonField.of(type)) /** @@ -1101,6 +1111,12 @@ private constructor( */ fun type(type: JsonField) = apply { this.type = type } + /** + * Used when sampling from a model. Dictates the structure of the messages passed + * into the model. Can either be a reference to a prebuilt trajectory (ie, + * `item.input_trajectory`), or a template with variable references to the `item` + * namespace. + */ fun inputMessages(inputMessages: InputMessages) = inputMessages(JsonField.of(inputMessages)) @@ -1259,7 +1275,7 @@ private constructor( (if (model.asKnown().isPresent) 1 else 0) + (samplingParams.asKnown().getOrNull()?.validity() ?: 0) - /** A EvalResponsesSource object describing a run data source configuration. */ + /** Determines what populates the `item` namespace in this run's data source. */ @JsonDeserialize(using = Source.Deserializer::class) @JsonSerialize(using = Source.Serializer::class) class Source @@ -2320,15 +2336,14 @@ private constructor( class Responses private constructor( private val type: JsonValue, - private val allowParallelToolCalls: JsonField, private val createdAfter: JsonField, private val createdBefore: JsonField, - private val hasToolCalls: JsonField, private val instructionsSearch: JsonField, private val metadata: JsonValue, private val model: JsonField, private val reasoningEffort: JsonField, private val temperature: JsonField, + private val tools: JsonField>, private val topP: JsonField, private val users: JsonField>, private val additionalProperties: MutableMap, @@ -2337,18 +2352,12 @@ private constructor( @JsonCreator private constructor( @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - @JsonProperty("allow_parallel_tool_calls") - @ExcludeMissing - allowParallelToolCalls: JsonField = JsonMissing.of(), @JsonProperty("created_after") @ExcludeMissing createdAfter: JsonField = JsonMissing.of(), @JsonProperty("created_before") @ExcludeMissing createdBefore: JsonField = JsonMissing.of(), - @JsonProperty("has_tool_calls") - @ExcludeMissing - hasToolCalls: JsonField = JsonMissing.of(), @JsonProperty("instructions_search") @ExcludeMissing instructionsSearch: JsonField = JsonMissing.of(), @@ -2364,6 +2373,9 @@ private constructor( @JsonProperty("temperature") @ExcludeMissing temperature: JsonField = JsonMissing.of(), + @JsonProperty("tools") + @ExcludeMissing + tools: JsonField> = JsonMissing.of(), @JsonProperty("top_p") @ExcludeMissing topP: JsonField = JsonMissing.of(), @@ -2372,15 +2384,14 @@ private constructor( users: JsonField> = JsonMissing.of(), ) : this( type, - allowParallelToolCalls, createdAfter, createdBefore, - hasToolCalls, instructionsSearch, metadata, model, reasoningEffort, temperature, + tools, topP, users, mutableMapOf(), @@ -2399,16 +2410,6 @@ private constructor( */ @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - /** - * Whether to allow parallel tool calls. This is a query parameter used to - * select responses. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). - */ - fun allowParallelToolCalls(): Optional = - allowParallelToolCalls.getOptional("allow_parallel_tool_calls") - /** * Only include items created after this timestamp (inclusive). This is a query * parameter used to select responses. @@ -2429,18 +2430,8 @@ private constructor( createdBefore.getOptional("created_before") /** - * Whether the response has tool calls. This is a query parameter used to select - * responses. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). - */ - fun hasToolCalls(): Optional = - hasToolCalls.getOptional("has_tool_calls") - - /** - * Optional search string for instructions. This is a query parameter used to - * select responses. + * Optional string to search the 'instructions' field. This is a query parameter + * used to select responses. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * (e.g. if the server responded with an unexpected value). @@ -2481,6 +2472,14 @@ private constructor( */ fun temperature(): Optional = temperature.getOptional("temperature") + /** + * List of tool names. This is a query parameter used to select responses. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun tools(): Optional> = tools.getOptional("tools") + /** * Nucleus sampling parameter. This is a query parameter used to select * responses. @@ -2498,16 +2497,6 @@ private constructor( */ fun users(): Optional> = users.getOptional("users") - /** - * Returns the raw JSON value of [allowParallelToolCalls]. - * - * Unlike [allowParallelToolCalls], this method doesn't throw if the JSON field - * has an unexpected type. - */ - @JsonProperty("allow_parallel_tool_calls") - @ExcludeMissing - fun _allowParallelToolCalls(): JsonField = allowParallelToolCalls - /** * Returns the raw JSON value of [createdAfter]. * @@ -2528,16 +2517,6 @@ private constructor( @ExcludeMissing fun _createdBefore(): JsonField = createdBefore - /** - * Returns the raw JSON value of [hasToolCalls]. - * - * Unlike [hasToolCalls], this method doesn't throw if the JSON field has an - * unexpected type. - */ - @JsonProperty("has_tool_calls") - @ExcludeMissing - fun _hasToolCalls(): JsonField = hasToolCalls - /** * Returns the raw JSON value of [instructionsSearch]. * @@ -2576,6 +2555,16 @@ private constructor( @ExcludeMissing fun _temperature(): JsonField = temperature + /** + * Returns the raw JSON value of [tools]. + * + * Unlike [tools], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("tools") + @ExcludeMissing + fun _tools(): JsonField> = tools + /** * Returns the raw JSON value of [topP]. * @@ -2618,15 +2607,14 @@ private constructor( class Builder internal constructor() { private var type: JsonValue = JsonValue.from("responses") - private var allowParallelToolCalls: JsonField = JsonMissing.of() private var createdAfter: JsonField = JsonMissing.of() private var createdBefore: JsonField = JsonMissing.of() - private var hasToolCalls: JsonField = JsonMissing.of() private var instructionsSearch: JsonField = JsonMissing.of() private var metadata: JsonValue = JsonMissing.of() private var model: JsonField = JsonMissing.of() private var reasoningEffort: JsonField = JsonMissing.of() private var temperature: JsonField = JsonMissing.of() + private var tools: JsonField>? = null private var topP: JsonField = JsonMissing.of() private var users: JsonField>? = null private var additionalProperties: MutableMap = @@ -2635,15 +2623,14 @@ private constructor( @JvmSynthetic internal fun from(responses: Responses) = apply { type = responses.type - allowParallelToolCalls = responses.allowParallelToolCalls createdAfter = responses.createdAfter createdBefore = responses.createdBefore - hasToolCalls = responses.hasToolCalls instructionsSearch = responses.instructionsSearch metadata = responses.metadata model = responses.model reasoningEffort = responses.reasoningEffort temperature = responses.temperature + tools = responses.tools.map { it.toMutableList() } topP = responses.topP users = responses.users.map { it.toMutableList() } additionalProperties = responses.additionalProperties.toMutableMap() @@ -2663,40 +2650,6 @@ private constructor( */ fun type(type: JsonValue) = apply { this.type = type } - /** - * Whether to allow parallel tool calls. This is a query parameter used to - * select responses. - */ - fun allowParallelToolCalls(allowParallelToolCalls: Boolean?) = - allowParallelToolCalls(JsonField.ofNullable(allowParallelToolCalls)) - - /** - * Alias for [Builder.allowParallelToolCalls]. - * - * This unboxed primitive overload exists for backwards compatibility. - */ - fun allowParallelToolCalls(allowParallelToolCalls: Boolean) = - allowParallelToolCalls(allowParallelToolCalls as Boolean?) - - /** - * Alias for calling [Builder.allowParallelToolCalls] with - * `allowParallelToolCalls.orElse(null)`. - */ - fun allowParallelToolCalls(allowParallelToolCalls: Optional) = - allowParallelToolCalls(allowParallelToolCalls.getOrNull()) - - /** - * Sets [Builder.allowParallelToolCalls] to an arbitrary JSON value. - * - * You should usually call [Builder.allowParallelToolCalls] with a - * well-typed [Boolean] value instead. This method is primarily for setting - * the field to an undocumented or not yet supported value. - */ - fun allowParallelToolCalls(allowParallelToolCalls: JsonField) = - apply { - this.allowParallelToolCalls = allowParallelToolCalls - } - /** * Only include items created after this timestamp (inclusive). This is a * query parameter used to select responses. @@ -2763,41 +2716,8 @@ private constructor( } /** - * Whether the response has tool calls. This is a query parameter used to - * select responses. - */ - fun hasToolCalls(hasToolCalls: Boolean?) = - hasToolCalls(JsonField.ofNullable(hasToolCalls)) - - /** - * Alias for [Builder.hasToolCalls]. - * - * This unboxed primitive overload exists for backwards compatibility. - */ - fun hasToolCalls(hasToolCalls: Boolean) = - hasToolCalls(hasToolCalls as Boolean?) - - /** - * Alias for calling [Builder.hasToolCalls] with - * `hasToolCalls.orElse(null)`. - */ - fun hasToolCalls(hasToolCalls: Optional) = - hasToolCalls(hasToolCalls.getOrNull()) - - /** - * Sets [Builder.hasToolCalls] to an arbitrary JSON value. - * - * You should usually call [Builder.hasToolCalls] with a well-typed - * [Boolean] value instead. This method is primarily for setting the field - * to an undocumented or not yet supported value. - */ - fun hasToolCalls(hasToolCalls: JsonField) = apply { - this.hasToolCalls = hasToolCalls - } - - /** - * Optional search string for instructions. This is a query parameter used - * to select responses. + * Optional string to search the 'instructions' field. This is a query + * parameter used to select responses. */ fun instructionsSearch(instructionsSearch: String?) = instructionsSearch(JsonField.ofNullable(instructionsSearch)) @@ -2899,6 +2819,38 @@ private constructor( this.temperature = temperature } + /** + * List of tool names. This is a query parameter used to select responses. + */ + fun tools(tools: List?) = tools(JsonField.ofNullable(tools)) + + /** Alias for calling [Builder.tools] with `tools.orElse(null)`. */ + fun tools(tools: Optional>) = tools(tools.getOrNull()) + + /** + * Sets [Builder.tools] to an arbitrary JSON value. + * + * You should usually call [Builder.tools] with a well-typed `List` + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun tools(tools: JsonField>) = apply { + this.tools = tools.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [tools]. + * + * @throws IllegalStateException if the field was previously set to a + * non-list. + */ + fun addTool(tool: String) = apply { + tools = + (tools ?: JsonField.of(mutableListOf())).also { + checkKnown("tools", it).add(tool) + } + } + /** * Nucleus sampling parameter. This is a query parameter used to select * responses. @@ -2987,15 +2939,14 @@ private constructor( fun build(): Responses = Responses( type, - allowParallelToolCalls, createdAfter, createdBefore, - hasToolCalls, instructionsSearch, metadata, model, reasoningEffort, temperature, + (tools ?: JsonMissing.of()).map { it.toImmutable() }, topP, (users ?: JsonMissing.of()).map { it.toImmutable() }, additionalProperties.toMutableMap(), @@ -3014,14 +2965,13 @@ private constructor( throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - allowParallelToolCalls() createdAfter() createdBefore() - hasToolCalls() instructionsSearch() model() reasoningEffort().ifPresent { it.validate() } temperature() + tools() topP() users() validated = true @@ -3044,14 +2994,13 @@ private constructor( @JvmSynthetic internal fun validity(): Int = type.let { if (it == JsonValue.from("responses")) 1 else 0 } + - (if (allowParallelToolCalls.asKnown().isPresent) 1 else 0) + (if (createdAfter.asKnown().isPresent) 1 else 0) + (if (createdBefore.asKnown().isPresent) 1 else 0) + - (if (hasToolCalls.asKnown().isPresent) 1 else 0) + (if (instructionsSearch.asKnown().isPresent) 1 else 0) + (if (model.asKnown().isPresent) 1 else 0) + (reasoningEffort.asKnown().getOrNull()?.validity() ?: 0) + (if (temperature.asKnown().isPresent) 1 else 0) + + (tools.asKnown().getOrNull()?.size ?: 0) + (if (topP.asKnown().isPresent) 1 else 0) + (users.asKnown().getOrNull()?.size ?: 0) @@ -3060,21 +3009,21 @@ private constructor( return true } - return /* spotless:off */ other is Responses && type == other.type && allowParallelToolCalls == other.allowParallelToolCalls && createdAfter == other.createdAfter && createdBefore == other.createdBefore && hasToolCalls == other.hasToolCalls && instructionsSearch == other.instructionsSearch && metadata == other.metadata && model == other.model && reasoningEffort == other.reasoningEffort && temperature == other.temperature && topP == other.topP && users == other.users && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is Responses && type == other.type && createdAfter == other.createdAfter && createdBefore == other.createdBefore && instructionsSearch == other.instructionsSearch && metadata == other.metadata && model == other.model && reasoningEffort == other.reasoningEffort && temperature == other.temperature && tools == other.tools && topP == other.topP && users == other.users && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(type, allowParallelToolCalls, createdAfter, createdBefore, hasToolCalls, instructionsSearch, metadata, model, reasoningEffort, temperature, topP, users, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(type, createdAfter, createdBefore, instructionsSearch, metadata, model, reasoningEffort, temperature, tools, topP, users, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "Responses{type=$type, allowParallelToolCalls=$allowParallelToolCalls, createdAfter=$createdAfter, createdBefore=$createdBefore, hasToolCalls=$hasToolCalls, instructionsSearch=$instructionsSearch, metadata=$metadata, model=$model, reasoningEffort=$reasoningEffort, temperature=$temperature, topP=$topP, users=$users, additionalProperties=$additionalProperties}" + "Responses{type=$type, createdAfter=$createdAfter, createdBefore=$createdBefore, instructionsSearch=$instructionsSearch, metadata=$metadata, model=$model, reasoningEffort=$reasoningEffort, temperature=$temperature, tools=$tools, topP=$topP, users=$users, additionalProperties=$additionalProperties}" } } - /** The type of run data source. Always `completions`. */ + /** The type of run data source. Always `responses`. */ class Type @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -3090,14 +3039,14 @@ private constructor( companion object { - @JvmField val COMPLETIONS = of("completions") + @JvmField val RESPONSES = of("responses") @JvmStatic fun of(value: String) = Type(JsonField.of(value)) } /** An enum containing [Type]'s known values. */ enum class Known { - COMPLETIONS + RESPONSES } /** @@ -3110,7 +3059,7 @@ private constructor( * - It was constructed with an arbitrary value using the [of] method. */ enum class Value { - COMPLETIONS, + RESPONSES, /** * An enum member indicating that [Type] was instantiated with an unknown value. */ @@ -3126,7 +3075,7 @@ private constructor( */ fun value(): Value = when (this) { - COMPLETIONS -> Value.COMPLETIONS + RESPONSES -> Value.RESPONSES else -> Value._UNKNOWN } @@ -3141,7 +3090,7 @@ private constructor( */ fun known(): Known = when (this) { - COMPLETIONS -> Known.COMPLETIONS + RESPONSES -> Known.RESPONSES else -> throw OpenAIInvalidDataException("Unknown Type: $value") } @@ -3199,6 +3148,12 @@ private constructor( override fun toString() = value.toString() } + /** + * Used when sampling from a model. Dictates the structure of the messages passed into + * the model. Can either be a reference to a prebuilt trajectory (ie, + * `item.input_trajectory`), or a template with variable references to the `item` + * namespace. + */ @JsonDeserialize(using = InputMessages.Deserializer::class) @JsonSerialize(using = InputMessages.Serializer::class) class InputMessages @@ -3388,7 +3343,7 @@ private constructor( /** * A list of chat messages forming the prompt or context. May include variable - * references to the "item" namespace, ie {{item.name}}. + * references to the `item` namespace, ie {{item.name}}. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * or is unexpectedly missing or null (e.g. if the server responded with an @@ -3461,7 +3416,7 @@ private constructor( /** * A list of chat messages forming the prompt or context. May include - * variable references to the "item" namespace, ie {{item.name}}. + * variable references to the `item` namespace, ie {{item.name}}. */ fun template(template: List) = template(JsonField.of(template)) @@ -5138,7 +5093,7 @@ private constructor( ) : this(itemReference, type, mutableMapOf()) /** - * A reference to a variable in the "item" namespace. Ie, "item.name" + * A reference to a variable in the `item` namespace. Ie, "item.name" * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * or is unexpectedly missing or null (e.g. if the server responded with an @@ -5210,7 +5165,7 @@ private constructor( additionalProperties = itemReference.additionalProperties.toMutableMap() } - /** A reference to a variable in the "item" namespace. Ie, "item.name" */ + /** A reference to a variable in the `item` namespace. Ie, "item.name" */ fun itemReference(itemReference: String) = itemReference(JsonField.of(itemReference)) diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateResponse.kt index b3ba2f56..327c71b7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateResponse.kt @@ -484,14 +484,9 @@ private constructor( fun fileIdJsonlDataSource(id: String) = jsonlDataSource(CreateEvalJsonlRunDataSource.Source.FileId.builder().id(id).build()) - /** - * Alias for calling [dataSource] with - * `DataSource.ofCreateEvalCompletionsRunDataSource(createEvalCompletionsRunDataSource)`. - */ - fun dataSource(createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource) = - dataSource( - DataSource.ofCreateEvalCompletionsRunDataSource(createEvalCompletionsRunDataSource) - ) + /** Alias for calling [dataSource] with `DataSource.ofCompletions(completions)`. */ + fun dataSource(completions: CreateEvalCompletionsRunDataSource) = + dataSource(DataSource.ofCompletions(completions)) /** * Alias for calling [dataSource] with the following: @@ -502,9 +497,7 @@ private constructor( * .build() * ``` */ - fun createEvalCompletionsRunDataSourceDataSource( - source: CreateEvalCompletionsRunDataSource.Source - ) = + fun completionsDataSource(source: CreateEvalCompletionsRunDataSource.Source) = dataSource( CreateEvalCompletionsRunDataSource.builder() .type(CreateEvalCompletionsRunDataSource.Type.COMPLETIONS) @@ -513,129 +506,125 @@ private constructor( ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with + * Alias for calling [completionsDataSource] with * `CreateEvalCompletionsRunDataSource.Source.ofFileContent(fileContent)`. */ - fun createEvalCompletionsRunDataSourceDataSource( + fun completionsDataSource( fileContent: CreateEvalCompletionsRunDataSource.Source.FileContent ) = - createEvalCompletionsRunDataSourceDataSource( + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.ofFileContent(fileContent) ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with the following: + * Alias for calling [completionsDataSource] with the following: * ```java * CreateEvalCompletionsRunDataSource.Source.FileContent.builder() * .content(content) * .build() * ``` */ - fun fileContentCreateEvalCompletionsRunDataSourceDataSource( + fun fileContentCompletionsDataSource( content: List ) = - createEvalCompletionsRunDataSourceDataSource( + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.FileContent.builder() .content(content) .build() ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with + * Alias for calling [completionsDataSource] with * `CreateEvalCompletionsRunDataSource.Source.ofFileId(fileId)`. */ - fun createEvalCompletionsRunDataSourceDataSource( - fileId: CreateEvalCompletionsRunDataSource.Source.FileId - ) = - createEvalCompletionsRunDataSourceDataSource( - CreateEvalCompletionsRunDataSource.Source.ofFileId(fileId) - ) + fun completionsDataSource(fileId: CreateEvalCompletionsRunDataSource.Source.FileId) = + completionsDataSource(CreateEvalCompletionsRunDataSource.Source.ofFileId(fileId)) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with the following: + * Alias for calling [completionsDataSource] with the following: * ```java * CreateEvalCompletionsRunDataSource.Source.FileId.builder() * .id(id) * .build() * ``` */ - fun fileIdCreateEvalCompletionsRunDataSourceDataSource(id: String) = - createEvalCompletionsRunDataSourceDataSource( + fun fileIdCompletionsDataSource(id: String) = + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.FileId.builder().id(id).build() ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with + * Alias for calling [completionsDataSource] with * `CreateEvalCompletionsRunDataSource.Source.ofStoredCompletions(storedCompletions)`. */ - fun createEvalCompletionsRunDataSourceDataSource( + fun completionsDataSource( storedCompletions: CreateEvalCompletionsRunDataSource.Source.StoredCompletions ) = - createEvalCompletionsRunDataSourceDataSource( + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.ofStoredCompletions(storedCompletions) ) - /** Alias for calling [dataSource] with `DataSource.ofCompletions(completions)`. */ - fun dataSource(completions: DataSource.Completions) = - dataSource(DataSource.ofCompletions(completions)) + /** Alias for calling [dataSource] with `DataSource.ofResponses(responses)`. */ + fun dataSource(responses: DataSource.Responses) = + dataSource(DataSource.ofResponses(responses)) /** * Alias for calling [dataSource] with the following: * ```java - * DataSource.Completions.builder() + * DataSource.Responses.builder() * .source(source) * .build() * ``` */ - fun completionsDataSource(source: DataSource.Completions.Source) = - dataSource(DataSource.Completions.builder().source(source).build()) + fun responsesDataSource(source: DataSource.Responses.Source) = + dataSource(DataSource.Responses.builder().source(source).build()) /** - * Alias for calling [completionsDataSource] with - * `DataSource.Completions.Source.ofFileContent(fileContent)`. + * Alias for calling [responsesDataSource] with + * `DataSource.Responses.Source.ofFileContent(fileContent)`. */ - fun completionsDataSource(fileContent: DataSource.Completions.Source.FileContent) = - completionsDataSource(DataSource.Completions.Source.ofFileContent(fileContent)) + fun responsesDataSource(fileContent: DataSource.Responses.Source.FileContent) = + responsesDataSource(DataSource.Responses.Source.ofFileContent(fileContent)) /** - * Alias for calling [completionsDataSource] with the following: + * Alias for calling [responsesDataSource] with the following: * ```java - * DataSource.Completions.Source.FileContent.builder() + * DataSource.Responses.Source.FileContent.builder() * .content(content) * .build() * ``` */ - fun fileContentCompletionsDataSource( - content: List + fun fileContentResponsesDataSource( + content: List ) = - completionsDataSource( - DataSource.Completions.Source.FileContent.builder().content(content).build() + responsesDataSource( + DataSource.Responses.Source.FileContent.builder().content(content).build() ) /** - * Alias for calling [completionsDataSource] with - * `DataSource.Completions.Source.ofFileId(fileId)`. + * Alias for calling [responsesDataSource] with + * `DataSource.Responses.Source.ofFileId(fileId)`. */ - fun completionsDataSource(fileId: DataSource.Completions.Source.FileId) = - completionsDataSource(DataSource.Completions.Source.ofFileId(fileId)) + fun responsesDataSource(fileId: DataSource.Responses.Source.FileId) = + responsesDataSource(DataSource.Responses.Source.ofFileId(fileId)) /** - * Alias for calling [completionsDataSource] with the following: + * Alias for calling [responsesDataSource] with the following: * ```java - * DataSource.Completions.Source.FileId.builder() + * DataSource.Responses.Source.FileId.builder() * .id(id) * .build() * ``` */ - fun fileIdCompletionsDataSource(id: String) = - completionsDataSource(DataSource.Completions.Source.FileId.builder().id(id).build()) + fun fileIdResponsesDataSource(id: String) = + responsesDataSource(DataSource.Responses.Source.FileId.builder().id(id).build()) /** - * Alias for calling [completionsDataSource] with - * `DataSource.Completions.Source.ofResponses(responses)`. + * Alias for calling [responsesDataSource] with + * `DataSource.Responses.Source.ofResponses(responses)`. */ - fun completionsDataSource(responses: DataSource.Completions.Source.Responses) = - completionsDataSource(DataSource.Completions.Source.ofResponses(responses)) + fun responsesDataSource(responses: DataSource.Responses.Source.InnerResponses) = + responsesDataSource(DataSource.Responses.Source.ofResponses(responses)) /** An object representing an error response from the Eval API. */ fun error(error: EvalApiError) = error(JsonField.of(error)) @@ -942,8 +931,8 @@ private constructor( class DataSource private constructor( private val jsonl: CreateEvalJsonlRunDataSource? = null, - private val createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource? = null, - private val completions: Completions? = null, + private val completions: CreateEvalCompletionsRunDataSource? = null, + private val responses: Responses? = null, private val _json: JsonValue? = null, ) { @@ -951,39 +940,35 @@ private constructor( fun jsonl(): Optional = Optional.ofNullable(jsonl) /** A CompletionsRunDataSource object describing a model sampling configuration. */ - fun createEvalCompletionsRunDataSource(): Optional = - Optional.ofNullable(createEvalCompletionsRunDataSource) + fun completions(): Optional = + Optional.ofNullable(completions) /** A ResponsesRunDataSource object describing a model sampling configuration. */ - fun completions(): Optional = Optional.ofNullable(completions) + fun responses(): Optional = Optional.ofNullable(responses) fun isJsonl(): Boolean = jsonl != null - fun isCreateEvalCompletionsRunDataSource(): Boolean = - createEvalCompletionsRunDataSource != null - fun isCompletions(): Boolean = completions != null + fun isResponses(): Boolean = responses != null + /** A JsonlRunDataSource object with that specifies a JSONL file that matches the eval */ fun asJsonl(): CreateEvalJsonlRunDataSource = jsonl.getOrThrow("jsonl") /** A CompletionsRunDataSource object describing a model sampling configuration. */ - fun asCreateEvalCompletionsRunDataSource(): CreateEvalCompletionsRunDataSource = - createEvalCompletionsRunDataSource.getOrThrow("createEvalCompletionsRunDataSource") + fun asCompletions(): CreateEvalCompletionsRunDataSource = + completions.getOrThrow("completions") /** A ResponsesRunDataSource object describing a model sampling configuration. */ - fun asCompletions(): Completions = completions.getOrThrow("completions") + fun asResponses(): Responses = responses.getOrThrow("responses") fun _json(): Optional = Optional.ofNullable(_json) fun accept(visitor: Visitor): T = when { jsonl != null -> visitor.visitJsonl(jsonl) - createEvalCompletionsRunDataSource != null -> - visitor.visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource - ) completions != null -> visitor.visitCompletions(completions) + responses != null -> visitor.visitResponses(responses) else -> visitor.unknown(_json) } @@ -1000,14 +985,12 @@ private constructor( jsonl.validate() } - override fun visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ) { - createEvalCompletionsRunDataSource.validate() + override fun visitCompletions(completions: CreateEvalCompletionsRunDataSource) { + completions.validate() } - override fun visitCompletions(completions: Completions) { - completions.validate() + override fun visitResponses(responses: Responses) { + responses.validate() } } ) @@ -1034,11 +1017,10 @@ private constructor( object : Visitor { override fun visitJsonl(jsonl: CreateEvalJsonlRunDataSource) = jsonl.validity() - override fun visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ) = createEvalCompletionsRunDataSource.validity() + override fun visitCompletions(completions: CreateEvalCompletionsRunDataSource) = + completions.validity() - override fun visitCompletions(completions: Completions) = completions.validity() + override fun visitResponses(responses: Responses) = responses.validity() override fun unknown(json: JsonValue?) = 0 } @@ -1049,17 +1031,16 @@ private constructor( return true } - return /* spotless:off */ other is DataSource && jsonl == other.jsonl && createEvalCompletionsRunDataSource == other.createEvalCompletionsRunDataSource && completions == other.completions /* spotless:on */ + return /* spotless:off */ other is DataSource && jsonl == other.jsonl && completions == other.completions && responses == other.responses /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(jsonl, createEvalCompletionsRunDataSource, completions) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(jsonl, completions, responses) /* spotless:on */ override fun toString(): String = when { jsonl != null -> "DataSource{jsonl=$jsonl}" - createEvalCompletionsRunDataSource != null -> - "DataSource{createEvalCompletionsRunDataSource=$createEvalCompletionsRunDataSource}" completions != null -> "DataSource{completions=$completions}" + responses != null -> "DataSource{responses=$responses}" _json != null -> "DataSource{_unknown=$_json}" else -> throw IllegalStateException("Invalid DataSource") } @@ -1073,13 +1054,11 @@ private constructor( /** A CompletionsRunDataSource object describing a model sampling configuration. */ @JvmStatic - fun ofCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ) = DataSource(createEvalCompletionsRunDataSource = createEvalCompletionsRunDataSource) + fun ofCompletions(completions: CreateEvalCompletionsRunDataSource) = + DataSource(completions = completions) /** A ResponsesRunDataSource object describing a model sampling configuration. */ - @JvmStatic - fun ofCompletions(completions: Completions) = DataSource(completions = completions) + @JvmStatic fun ofResponses(responses: Responses) = DataSource(responses = responses) } /** @@ -1093,12 +1072,10 @@ private constructor( fun visitJsonl(jsonl: CreateEvalJsonlRunDataSource): T /** A CompletionsRunDataSource object describing a model sampling configuration. */ - fun visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ): T + fun visitCompletions(completions: CreateEvalCompletionsRunDataSource): T /** A ResponsesRunDataSource object describing a model sampling configuration. */ - fun visitCompletions(completions: Completions): T + fun visitResponses(responses: Responses): T /** * Maps an unknown variant of [DataSource] to a value of type [T]. @@ -1128,36 +1105,17 @@ private constructor( ?: DataSource(_json = json) } "completions" -> { - val bestMatches = - sequenceOf( - tryDeserialize( - node, - jacksonTypeRef(), - ) - ?.let { - DataSource( - createEvalCompletionsRunDataSource = it, - _json = json, - ) - }, - tryDeserialize(node, jacksonTypeRef())?.let { - DataSource(completions = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing from - // boolean). - 0 -> DataSource(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then use - // the first completely valid match, or simply the first match if none - // are completely valid. - else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { DataSource(completions = it, _json = json) } + ?: DataSource(_json = json) + } + "responses" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DataSource(responses = it, _json = json) + } ?: DataSource(_json = json) } } @@ -1174,9 +1132,8 @@ private constructor( ) { when { value.jsonl != null -> generator.writeObject(value.jsonl) - value.createEvalCompletionsRunDataSource != null -> - generator.writeObject(value.createEvalCompletionsRunDataSource) value.completions != null -> generator.writeObject(value.completions) + value.responses != null -> generator.writeObject(value.responses) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid DataSource") } @@ -1184,7 +1141,7 @@ private constructor( } /** A ResponsesRunDataSource object describing a model sampling configuration. */ - class Completions + class Responses private constructor( private val source: JsonField, private val type: JsonValue, @@ -1210,7 +1167,7 @@ private constructor( ) : this(source, type, inputMessages, model, samplingParams, mutableMapOf()) /** - * A EvalResponsesSource object describing a run data source configuration. + * Determines what populates the `item` namespace in this run's data source. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected @@ -1219,11 +1176,11 @@ private constructor( fun source(): Source = source.getRequired("source") /** - * The type of run data source. Always `completions`. + * The type of run data source. Always `responses`. * * Expected to always return the following: * ```java - * JsonValue.from("completions") + * JsonValue.from("responses") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1232,6 +1189,11 @@ private constructor( @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** + * Used when sampling from a model. Dictates the structure of the messages passed into + * the model. Can either be a reference to a prebuilt trajectory (ie, + * `item.input_trajectory`), or a template with variable references to the `item` + * namespace. + * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ @@ -1302,7 +1264,7 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [Completions]. + * Returns a mutable builder for constructing an instance of [Responses]. * * The following fields are required: * ```java @@ -1312,27 +1274,27 @@ private constructor( @JvmStatic fun builder() = Builder() } - /** A builder for [Completions]. */ + /** A builder for [Responses]. */ class Builder internal constructor() { private var source: JsonField? = null - private var type: JsonValue = JsonValue.from("completions") + private var type: JsonValue = JsonValue.from("responses") private var inputMessages: JsonField = JsonMissing.of() private var model: JsonField = JsonMissing.of() private var samplingParams: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(completions: Completions) = apply { - source = completions.source - type = completions.type - inputMessages = completions.inputMessages - model = completions.model - samplingParams = completions.samplingParams - additionalProperties = completions.additionalProperties.toMutableMap() + internal fun from(responses: Responses) = apply { + source = responses.source + type = responses.type + inputMessages = responses.inputMessages + model = responses.model + samplingParams = responses.samplingParams + additionalProperties = responses.additionalProperties.toMutableMap() } - /** A EvalResponsesSource object describing a run data source configuration. */ + /** Determines what populates the `item` namespace in this run's data source. */ fun source(source: Source) = source(JsonField.of(source)) /** @@ -1373,7 +1335,7 @@ private constructor( fun fileIdSource(id: String) = source(Source.FileId.builder().id(id).build()) /** Alias for calling [source] with `Source.ofResponses(responses)`. */ - fun source(responses: Source.Responses) = source(Source.ofResponses(responses)) + fun source(responses: Source.InnerResponses) = source(Source.ofResponses(responses)) /** * Sets the field to an arbitrary JSON value. @@ -1381,7 +1343,7 @@ private constructor( * It is usually unnecessary to call this method because the field defaults to the * following: * ```java - * JsonValue.from("completions") + * JsonValue.from("responses") * ``` * * This method is primarily for setting the field to an undocumented or not yet @@ -1389,6 +1351,12 @@ private constructor( */ fun type(type: JsonValue) = apply { this.type = type } + /** + * Used when sampling from a model. Dictates the structure of the messages passed + * into the model. Can either be a reference to a prebuilt trajectory (ie, + * `item.input_trajectory`), or a template with variable references to the `item` + * namespace. + */ fun inputMessages(inputMessages: InputMessages) = inputMessages(JsonField.of(inputMessages)) @@ -1487,7 +1455,7 @@ private constructor( } /** - * Returns an immutable instance of [Completions]. + * Returns an immutable instance of [Responses]. * * Further updates to this [Builder] will not mutate the returned instance. * @@ -1498,8 +1466,8 @@ private constructor( * * @throws IllegalStateException if any required field is unset. */ - fun build(): Completions = - Completions( + fun build(): Responses = + Responses( checkRequired("source", source), type, inputMessages, @@ -1511,14 +1479,14 @@ private constructor( private var validated: Boolean = false - fun validate(): Completions = apply { + fun validate(): Responses = apply { if (validated) { return@apply } source().validate() _type().let { - if (it != JsonValue.from("completions")) { + if (it != JsonValue.from("responses")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") } } @@ -1545,19 +1513,19 @@ private constructor( @JvmSynthetic internal fun validity(): Int = (source.asKnown().getOrNull()?.validity() ?: 0) + - type.let { if (it == JsonValue.from("completions")) 1 else 0 } + + type.let { if (it == JsonValue.from("responses")) 1 else 0 } + (inputMessages.asKnown().getOrNull()?.validity() ?: 0) + (if (model.asKnown().isPresent) 1 else 0) + (samplingParams.asKnown().getOrNull()?.validity() ?: 0) - /** A EvalResponsesSource object describing a run data source configuration. */ + /** Determines what populates the `item` namespace in this run's data source. */ @JsonDeserialize(using = Source.Deserializer::class) @JsonSerialize(using = Source.Serializer::class) class Source private constructor( private val fileContent: FileContent? = null, private val fileId: FileId? = null, - private val responses: Responses? = null, + private val responses: InnerResponses? = null, private val _json: JsonValue? = null, ) { @@ -1566,7 +1534,7 @@ private constructor( fun fileId(): Optional = Optional.ofNullable(fileId) /** A EvalResponsesSource object describing a run data source configuration. */ - fun responses(): Optional = Optional.ofNullable(responses) + fun responses(): Optional = Optional.ofNullable(responses) fun isFileContent(): Boolean = fileContent != null @@ -1579,7 +1547,7 @@ private constructor( fun asFileId(): FileId = fileId.getOrThrow("fileId") /** A EvalResponsesSource object describing a run data source configuration. */ - fun asResponses(): Responses = responses.getOrThrow("responses") + fun asResponses(): InnerResponses = responses.getOrThrow("responses") fun _json(): Optional = Optional.ofNullable(_json) @@ -1608,7 +1576,7 @@ private constructor( fileId.validate() } - override fun visitResponses(responses: Responses) { + override fun visitResponses(responses: InnerResponses) { responses.validate() } } @@ -1639,7 +1607,8 @@ private constructor( override fun visitFileId(fileId: FileId) = fileId.validity() - override fun visitResponses(responses: Responses) = responses.validity() + override fun visitResponses(responses: InnerResponses) = + responses.validity() override fun unknown(json: JsonValue?) = 0 } @@ -1672,7 +1641,8 @@ private constructor( @JvmStatic fun ofFileId(fileId: FileId) = Source(fileId = fileId) /** A EvalResponsesSource object describing a run data source configuration. */ - @JvmStatic fun ofResponses(responses: Responses) = Source(responses = responses) + @JvmStatic + fun ofResponses(responses: InnerResponses) = Source(responses = responses) } /** @@ -1686,7 +1656,7 @@ private constructor( fun visitFileId(fileId: FileId): T /** A EvalResponsesSource object describing a run data source configuration. */ - fun visitResponses(responses: Responses): T + fun visitResponses(responses: InnerResponses): T /** * Maps an unknown variant of [Source] to a value of type [T]. @@ -1721,7 +1691,7 @@ private constructor( } ?: Source(_json = json) } "responses" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { + return tryDeserialize(node, jacksonTypeRef())?.let { Source(responses = it, _json = json) } ?: Source(_json = json) } @@ -2608,18 +2578,17 @@ private constructor( } /** A EvalResponsesSource object describing a run data source configuration. */ - class Responses + class InnerResponses private constructor( private val type: JsonValue, - private val allowParallelToolCalls: JsonField, private val createdAfter: JsonField, private val createdBefore: JsonField, - private val hasToolCalls: JsonField, private val instructionsSearch: JsonField, private val metadata: JsonValue, private val model: JsonField, private val reasoningEffort: JsonField, private val temperature: JsonField, + private val tools: JsonField>, private val topP: JsonField, private val users: JsonField>, private val additionalProperties: MutableMap, @@ -2628,18 +2597,12 @@ private constructor( @JsonCreator private constructor( @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - @JsonProperty("allow_parallel_tool_calls") - @ExcludeMissing - allowParallelToolCalls: JsonField = JsonMissing.of(), @JsonProperty("created_after") @ExcludeMissing createdAfter: JsonField = JsonMissing.of(), @JsonProperty("created_before") @ExcludeMissing createdBefore: JsonField = JsonMissing.of(), - @JsonProperty("has_tool_calls") - @ExcludeMissing - hasToolCalls: JsonField = JsonMissing.of(), @JsonProperty("instructions_search") @ExcludeMissing instructionsSearch: JsonField = JsonMissing.of(), @@ -2655,6 +2618,9 @@ private constructor( @JsonProperty("temperature") @ExcludeMissing temperature: JsonField = JsonMissing.of(), + @JsonProperty("tools") + @ExcludeMissing + tools: JsonField> = JsonMissing.of(), @JsonProperty("top_p") @ExcludeMissing topP: JsonField = JsonMissing.of(), @@ -2663,15 +2629,14 @@ private constructor( users: JsonField> = JsonMissing.of(), ) : this( type, - allowParallelToolCalls, createdAfter, createdBefore, - hasToolCalls, instructionsSearch, metadata, model, reasoningEffort, temperature, + tools, topP, users, mutableMapOf(), @@ -2690,16 +2655,6 @@ private constructor( */ @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - /** - * Whether to allow parallel tool calls. This is a query parameter used to - * select responses. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). - */ - fun allowParallelToolCalls(): Optional = - allowParallelToolCalls.getOptional("allow_parallel_tool_calls") - /** * Only include items created after this timestamp (inclusive). This is a query * parameter used to select responses. @@ -2720,18 +2675,8 @@ private constructor( createdBefore.getOptional("created_before") /** - * Whether the response has tool calls. This is a query parameter used to select - * responses. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). - */ - fun hasToolCalls(): Optional = - hasToolCalls.getOptional("has_tool_calls") - - /** - * Optional search string for instructions. This is a query parameter used to - * select responses. + * Optional string to search the 'instructions' field. This is a query parameter + * used to select responses. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * (e.g. if the server responded with an unexpected value). @@ -2772,6 +2717,14 @@ private constructor( */ fun temperature(): Optional = temperature.getOptional("temperature") + /** + * List of tool names. This is a query parameter used to select responses. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun tools(): Optional> = tools.getOptional("tools") + /** * Nucleus sampling parameter. This is a query parameter used to select * responses. @@ -2789,16 +2742,6 @@ private constructor( */ fun users(): Optional> = users.getOptional("users") - /** - * Returns the raw JSON value of [allowParallelToolCalls]. - * - * Unlike [allowParallelToolCalls], this method doesn't throw if the JSON field - * has an unexpected type. - */ - @JsonProperty("allow_parallel_tool_calls") - @ExcludeMissing - fun _allowParallelToolCalls(): JsonField = allowParallelToolCalls - /** * Returns the raw JSON value of [createdAfter]. * @@ -2819,16 +2762,6 @@ private constructor( @ExcludeMissing fun _createdBefore(): JsonField = createdBefore - /** - * Returns the raw JSON value of [hasToolCalls]. - * - * Unlike [hasToolCalls], this method doesn't throw if the JSON field has an - * unexpected type. - */ - @JsonProperty("has_tool_calls") - @ExcludeMissing - fun _hasToolCalls(): JsonField = hasToolCalls - /** * Returns the raw JSON value of [instructionsSearch]. * @@ -2867,6 +2800,16 @@ private constructor( @ExcludeMissing fun _temperature(): JsonField = temperature + /** + * Returns the raw JSON value of [tools]. + * + * Unlike [tools], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("tools") + @ExcludeMissing + fun _tools(): JsonField> = tools + /** * Returns the raw JSON value of [topP]. * @@ -2900,44 +2843,44 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [Responses]. + * Returns a mutable builder for constructing an instance of + * [InnerResponses]. */ @JvmStatic fun builder() = Builder() } - /** A builder for [Responses]. */ + /** A builder for [InnerResponses]. */ class Builder internal constructor() { private var type: JsonValue = JsonValue.from("responses") - private var allowParallelToolCalls: JsonField = JsonMissing.of() private var createdAfter: JsonField = JsonMissing.of() private var createdBefore: JsonField = JsonMissing.of() - private var hasToolCalls: JsonField = JsonMissing.of() private var instructionsSearch: JsonField = JsonMissing.of() private var metadata: JsonValue = JsonMissing.of() private var model: JsonField = JsonMissing.of() private var reasoningEffort: JsonField = JsonMissing.of() private var temperature: JsonField = JsonMissing.of() + private var tools: JsonField>? = null private var topP: JsonField = JsonMissing.of() private var users: JsonField>? = null private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(responses: Responses) = apply { - type = responses.type - allowParallelToolCalls = responses.allowParallelToolCalls - createdAfter = responses.createdAfter - createdBefore = responses.createdBefore - hasToolCalls = responses.hasToolCalls - instructionsSearch = responses.instructionsSearch - metadata = responses.metadata - model = responses.model - reasoningEffort = responses.reasoningEffort - temperature = responses.temperature - topP = responses.topP - users = responses.users.map { it.toMutableList() } - additionalProperties = responses.additionalProperties.toMutableMap() + internal fun from(innerResponses: InnerResponses) = apply { + type = innerResponses.type + createdAfter = innerResponses.createdAfter + createdBefore = innerResponses.createdBefore + instructionsSearch = innerResponses.instructionsSearch + metadata = innerResponses.metadata + model = innerResponses.model + reasoningEffort = innerResponses.reasoningEffort + temperature = innerResponses.temperature + tools = innerResponses.tools.map { it.toMutableList() } + topP = innerResponses.topP + users = innerResponses.users.map { it.toMutableList() } + additionalProperties = + innerResponses.additionalProperties.toMutableMap() } /** @@ -2954,40 +2897,6 @@ private constructor( */ fun type(type: JsonValue) = apply { this.type = type } - /** - * Whether to allow parallel tool calls. This is a query parameter used to - * select responses. - */ - fun allowParallelToolCalls(allowParallelToolCalls: Boolean?) = - allowParallelToolCalls(JsonField.ofNullable(allowParallelToolCalls)) - - /** - * Alias for [Builder.allowParallelToolCalls]. - * - * This unboxed primitive overload exists for backwards compatibility. - */ - fun allowParallelToolCalls(allowParallelToolCalls: Boolean) = - allowParallelToolCalls(allowParallelToolCalls as Boolean?) - - /** - * Alias for calling [Builder.allowParallelToolCalls] with - * `allowParallelToolCalls.orElse(null)`. - */ - fun allowParallelToolCalls(allowParallelToolCalls: Optional) = - allowParallelToolCalls(allowParallelToolCalls.getOrNull()) - - /** - * Sets [Builder.allowParallelToolCalls] to an arbitrary JSON value. - * - * You should usually call [Builder.allowParallelToolCalls] with a - * well-typed [Boolean] value instead. This method is primarily for setting - * the field to an undocumented or not yet supported value. - */ - fun allowParallelToolCalls(allowParallelToolCalls: JsonField) = - apply { - this.allowParallelToolCalls = allowParallelToolCalls - } - /** * Only include items created after this timestamp (inclusive). This is a * query parameter used to select responses. @@ -3054,41 +2963,8 @@ private constructor( } /** - * Whether the response has tool calls. This is a query parameter used to - * select responses. - */ - fun hasToolCalls(hasToolCalls: Boolean?) = - hasToolCalls(JsonField.ofNullable(hasToolCalls)) - - /** - * Alias for [Builder.hasToolCalls]. - * - * This unboxed primitive overload exists for backwards compatibility. - */ - fun hasToolCalls(hasToolCalls: Boolean) = - hasToolCalls(hasToolCalls as Boolean?) - - /** - * Alias for calling [Builder.hasToolCalls] with - * `hasToolCalls.orElse(null)`. - */ - fun hasToolCalls(hasToolCalls: Optional) = - hasToolCalls(hasToolCalls.getOrNull()) - - /** - * Sets [Builder.hasToolCalls] to an arbitrary JSON value. - * - * You should usually call [Builder.hasToolCalls] with a well-typed - * [Boolean] value instead. This method is primarily for setting the field - * to an undocumented or not yet supported value. - */ - fun hasToolCalls(hasToolCalls: JsonField) = apply { - this.hasToolCalls = hasToolCalls - } - - /** - * Optional search string for instructions. This is a query parameter used - * to select responses. + * Optional string to search the 'instructions' field. This is a query + * parameter used to select responses. */ fun instructionsSearch(instructionsSearch: String?) = instructionsSearch(JsonField.ofNullable(instructionsSearch)) @@ -3190,6 +3066,38 @@ private constructor( this.temperature = temperature } + /** + * List of tool names. This is a query parameter used to select responses. + */ + fun tools(tools: List?) = tools(JsonField.ofNullable(tools)) + + /** Alias for calling [Builder.tools] with `tools.orElse(null)`. */ + fun tools(tools: Optional>) = tools(tools.getOrNull()) + + /** + * Sets [Builder.tools] to an arbitrary JSON value. + * + * You should usually call [Builder.tools] with a well-typed `List` + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun tools(tools: JsonField>) = apply { + this.tools = tools.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [tools]. + * + * @throws IllegalStateException if the field was previously set to a + * non-list. + */ + fun addTool(tool: String) = apply { + tools = + (tools ?: JsonField.of(mutableListOf())).also { + checkKnown("tools", it).add(tool) + } + } + /** * Nucleus sampling parameter. This is a query parameter used to select * responses. @@ -3271,22 +3179,21 @@ private constructor( } /** - * Returns an immutable instance of [Responses]. + * Returns an immutable instance of [InnerResponses]. * * Further updates to this [Builder] will not mutate the returned instance. */ - fun build(): Responses = - Responses( + fun build(): InnerResponses = + InnerResponses( type, - allowParallelToolCalls, createdAfter, createdBefore, - hasToolCalls, instructionsSearch, metadata, model, reasoningEffort, temperature, + (tools ?: JsonMissing.of()).map { it.toImmutable() }, topP, (users ?: JsonMissing.of()).map { it.toImmutable() }, additionalProperties.toMutableMap(), @@ -3295,7 +3202,7 @@ private constructor( private var validated: Boolean = false - fun validate(): Responses = apply { + fun validate(): InnerResponses = apply { if (validated) { return@apply } @@ -3305,14 +3212,13 @@ private constructor( throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - allowParallelToolCalls() createdAfter() createdBefore() - hasToolCalls() instructionsSearch() model() reasoningEffort().ifPresent { it.validate() } temperature() + tools() topP() users() validated = true @@ -3335,14 +3241,13 @@ private constructor( @JvmSynthetic internal fun validity(): Int = type.let { if (it == JsonValue.from("responses")) 1 else 0 } + - (if (allowParallelToolCalls.asKnown().isPresent) 1 else 0) + (if (createdAfter.asKnown().isPresent) 1 else 0) + (if (createdBefore.asKnown().isPresent) 1 else 0) + - (if (hasToolCalls.asKnown().isPresent) 1 else 0) + (if (instructionsSearch.asKnown().isPresent) 1 else 0) + (if (model.asKnown().isPresent) 1 else 0) + (reasoningEffort.asKnown().getOrNull()?.validity() ?: 0) + (if (temperature.asKnown().isPresent) 1 else 0) + + (tools.asKnown().getOrNull()?.size ?: 0) + (if (topP.asKnown().isPresent) 1 else 0) + (users.asKnown().getOrNull()?.size ?: 0) @@ -3351,20 +3256,26 @@ private constructor( return true } - return /* spotless:off */ other is Responses && type == other.type && allowParallelToolCalls == other.allowParallelToolCalls && createdAfter == other.createdAfter && createdBefore == other.createdBefore && hasToolCalls == other.hasToolCalls && instructionsSearch == other.instructionsSearch && metadata == other.metadata && model == other.model && reasoningEffort == other.reasoningEffort && temperature == other.temperature && topP == other.topP && users == other.users && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is InnerResponses && type == other.type && createdAfter == other.createdAfter && createdBefore == other.createdBefore && instructionsSearch == other.instructionsSearch && metadata == other.metadata && model == other.model && reasoningEffort == other.reasoningEffort && temperature == other.temperature && tools == other.tools && topP == other.topP && users == other.users && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(type, allowParallelToolCalls, createdAfter, createdBefore, hasToolCalls, instructionsSearch, metadata, model, reasoningEffort, temperature, topP, users, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(type, createdAfter, createdBefore, instructionsSearch, metadata, model, reasoningEffort, temperature, tools, topP, users, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "Responses{type=$type, allowParallelToolCalls=$allowParallelToolCalls, createdAfter=$createdAfter, createdBefore=$createdBefore, hasToolCalls=$hasToolCalls, instructionsSearch=$instructionsSearch, metadata=$metadata, model=$model, reasoningEffort=$reasoningEffort, temperature=$temperature, topP=$topP, users=$users, additionalProperties=$additionalProperties}" + "InnerResponses{type=$type, createdAfter=$createdAfter, createdBefore=$createdBefore, instructionsSearch=$instructionsSearch, metadata=$metadata, model=$model, reasoningEffort=$reasoningEffort, temperature=$temperature, tools=$tools, topP=$topP, users=$users, additionalProperties=$additionalProperties}" } } + /** + * Used when sampling from a model. Dictates the structure of the messages passed into + * the model. Can either be a reference to a prebuilt trajectory (ie, + * `item.input_trajectory`), or a template with variable references to the `item` + * namespace. + */ @JsonDeserialize(using = InputMessages.Deserializer::class) @JsonSerialize(using = InputMessages.Serializer::class) class InputMessages @@ -3554,7 +3465,7 @@ private constructor( /** * A list of chat messages forming the prompt or context. May include variable - * references to the "item" namespace, ie {{item.name}}. + * references to the `item` namespace, ie {{item.name}}. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * or is unexpectedly missing or null (e.g. if the server responded with an @@ -3627,7 +3538,7 @@ private constructor( /** * A list of chat messages forming the prompt or context. May include - * variable references to the "item" namespace, ie {{item.name}}. + * variable references to the `item` namespace, ie {{item.name}}. */ fun template(template: List) = template(JsonField.of(template)) @@ -5304,7 +5215,7 @@ private constructor( ) : this(itemReference, type, mutableMapOf()) /** - * A reference to a variable in the "item" namespace. Ie, "item.name" + * A reference to a variable in the `item` namespace. Ie, "item.name" * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * or is unexpectedly missing or null (e.g. if the server responded with an @@ -5376,7 +5287,7 @@ private constructor( additionalProperties = itemReference.additionalProperties.toMutableMap() } - /** A reference to a variable in the "item" namespace. Ie, "item.name" */ + /** A reference to a variable in the `item` namespace. Ie, "item.name" */ fun itemReference(itemReference: String) = itemReference(JsonField.of(itemReference)) @@ -5781,7 +5692,7 @@ private constructor( return true } - return /* spotless:off */ other is Completions && source == other.source && type == other.type && inputMessages == other.inputMessages && model == other.model && samplingParams == other.samplingParams && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is Responses && source == other.source && type == other.type && inputMessages == other.inputMessages && model == other.model && samplingParams == other.samplingParams && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ @@ -5791,7 +5702,7 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "Completions{source=$source, type=$type, inputMessages=$inputMessages, model=$model, samplingParams=$samplingParams, additionalProperties=$additionalProperties}" + "Responses{source=$source, type=$type, inputMessages=$inputMessages, model=$model, samplingParams=$samplingParams, additionalProperties=$additionalProperties}" } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunDeleteParams.kt index 2214b0d2..784ca9c0 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunDeleteParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunDeleteParams.kt @@ -10,12 +10,13 @@ import com.openai.core.http.QueryParams import com.openai.core.toImmutable import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Delete an eval run. */ class RunDeleteParams private constructor( private val evalId: String, - private val runId: String, + private val runId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, @@ -23,7 +24,7 @@ private constructor( fun evalId(): String = evalId - fun runId(): String = runId + fun runId(): Optional = Optional.ofNullable(runId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -41,7 +42,6 @@ private constructor( * The following fields are required: * ```java * .evalId() - * .runId() * ``` */ @JvmStatic fun builder() = Builder() @@ -67,7 +67,10 @@ private constructor( fun evalId(evalId: String) = apply { this.evalId = evalId } - fun runId(runId: String) = apply { this.runId = runId } + fun runId(runId: String?) = apply { this.runId = runId } + + /** Alias for calling [Builder.runId] with `runId.orElse(null)`. */ + fun runId(runId: Optional) = runId(runId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -197,7 +200,6 @@ private constructor( * The following fields are required: * ```java * .evalId() - * .runId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -205,7 +207,7 @@ private constructor( fun build(): RunDeleteParams = RunDeleteParams( checkRequired("evalId", evalId), - checkRequired("runId", runId), + runId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -218,7 +220,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> evalId - 1 -> runId + 1 -> runId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListPage.kt index ca22d061..4e9fc0d7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListPage.kt @@ -2,12 +2,12 @@ package com.openai.models.evals.runs +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.evals.RunService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [RunService.list] */ @@ -16,7 +16,7 @@ private constructor( private val service: RunService, private val params: RunListParams, private val response: RunListPageResponse, -) { +) : Page { /** * Delegates to [RunListPageResponse], but gracefully handles missing data. @@ -33,19 +33,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): RunListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + override fun nextPage(): RunListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): RunListParams = params @@ -114,25 +111,6 @@ private constructor( ) } - class AutoPager(private val firstPage: RunListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListPageAsync.kt index f159f392..a914663d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListPageAsync.kt @@ -2,22 +2,24 @@ package com.openai.models.evals.runs +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.evals.RunServiceAsync import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [RunServiceAsync.list] */ class RunListPageAsync private constructor( private val service: RunServiceAsync, + private val streamHandlerExecutor: Executor, private val params: RunListParams, private val response: RunListPageResponse, -) { +) : PageAsync { /** * Delegates to [RunListPageResponse], but gracefully handles missing data. @@ -34,22 +36,17 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): RunListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = + AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): RunListParams = params @@ -67,6 +64,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -78,18 +76,24 @@ private constructor( class Builder internal constructor() { private var service: RunServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: RunListParams? = null private var response: RunListPageResponse? = null @JvmSynthetic internal fun from(runListPageAsync: RunListPageAsync) = apply { service = runListPageAsync.service + streamHandlerExecutor = runListPageAsync.streamHandlerExecutor params = runListPageAsync.params response = runListPageAsync.response } fun service(service: RunServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: RunListParams) = apply { this.params = params } @@ -104,6 +108,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -113,50 +118,22 @@ private constructor( fun build(): RunListPageAsync = RunListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: RunListPageAsync) { - - fun forEach( - action: Predicate, - executor: Executor, - ): CompletableFuture { - fun CompletableFuture>.forEach( - action: (RunListResponse) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is RunListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is RunListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "RunListPageAsync{service=$service, params=$params, response=$response}" + "RunListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListParams.kt index b825f64b..56035b50 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListParams.kt @@ -6,7 +6,6 @@ import com.fasterxml.jackson.annotation.JsonCreator import com.openai.core.Enum import com.openai.core.JsonField 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.errors.OpenAIInvalidDataException @@ -17,7 +16,7 @@ import kotlin.jvm.optionals.getOrNull /** Get a list of runs for an evaluation. */ class RunListParams private constructor( - private val evalId: String, + private val evalId: String?, private val after: String?, private val limit: Long?, private val order: Order?, @@ -26,7 +25,7 @@ private constructor( private val additionalQueryParams: QueryParams, ) : Params { - fun evalId(): String = evalId + fun evalId(): Optional = Optional.ofNullable(evalId) /** Identifier for the last run from the previous pagination request. */ fun after(): Optional = Optional.ofNullable(after) @@ -53,14 +52,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [RunListParams]. - * - * The following fields are required: - * ```java - * .evalId() - * ``` - */ + @JvmStatic fun none(): RunListParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [RunListParams]. */ @JvmStatic fun builder() = Builder() } @@ -86,7 +80,10 @@ private constructor( additionalQueryParams = runListParams.additionalQueryParams.toBuilder() } - fun evalId(evalId: String) = apply { this.evalId = evalId } + fun evalId(evalId: String?) = apply { this.evalId = evalId } + + /** Alias for calling [Builder.evalId] with `evalId.orElse(null)`. */ + fun evalId(evalId: Optional) = evalId(evalId.getOrNull()) /** Identifier for the last run from the previous pagination request. */ fun after(after: String?) = apply { this.after = after } @@ -227,17 +224,10 @@ private constructor( * Returns an immutable instance of [RunListParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .evalId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): RunListParams = RunListParams( - checkRequired("evalId", evalId), + evalId, after, limit, order, @@ -249,7 +239,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> evalId + 0 -> evalId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListResponse.kt index 2cfc8ff7..1b4df539 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListResponse.kt @@ -484,14 +484,9 @@ private constructor( fun fileIdJsonlDataSource(id: String) = jsonlDataSource(CreateEvalJsonlRunDataSource.Source.FileId.builder().id(id).build()) - /** - * Alias for calling [dataSource] with - * `DataSource.ofCreateEvalCompletionsRunDataSource(createEvalCompletionsRunDataSource)`. - */ - fun dataSource(createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource) = - dataSource( - DataSource.ofCreateEvalCompletionsRunDataSource(createEvalCompletionsRunDataSource) - ) + /** Alias for calling [dataSource] with `DataSource.ofCompletions(completions)`. */ + fun dataSource(completions: CreateEvalCompletionsRunDataSource) = + dataSource(DataSource.ofCompletions(completions)) /** * Alias for calling [dataSource] with the following: @@ -502,9 +497,7 @@ private constructor( * .build() * ``` */ - fun createEvalCompletionsRunDataSourceDataSource( - source: CreateEvalCompletionsRunDataSource.Source - ) = + fun completionsDataSource(source: CreateEvalCompletionsRunDataSource.Source) = dataSource( CreateEvalCompletionsRunDataSource.builder() .type(CreateEvalCompletionsRunDataSource.Type.COMPLETIONS) @@ -513,129 +506,125 @@ private constructor( ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with + * Alias for calling [completionsDataSource] with * `CreateEvalCompletionsRunDataSource.Source.ofFileContent(fileContent)`. */ - fun createEvalCompletionsRunDataSourceDataSource( + fun completionsDataSource( fileContent: CreateEvalCompletionsRunDataSource.Source.FileContent ) = - createEvalCompletionsRunDataSourceDataSource( + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.ofFileContent(fileContent) ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with the following: + * Alias for calling [completionsDataSource] with the following: * ```java * CreateEvalCompletionsRunDataSource.Source.FileContent.builder() * .content(content) * .build() * ``` */ - fun fileContentCreateEvalCompletionsRunDataSourceDataSource( + fun fileContentCompletionsDataSource( content: List ) = - createEvalCompletionsRunDataSourceDataSource( + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.FileContent.builder() .content(content) .build() ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with + * Alias for calling [completionsDataSource] with * `CreateEvalCompletionsRunDataSource.Source.ofFileId(fileId)`. */ - fun createEvalCompletionsRunDataSourceDataSource( - fileId: CreateEvalCompletionsRunDataSource.Source.FileId - ) = - createEvalCompletionsRunDataSourceDataSource( - CreateEvalCompletionsRunDataSource.Source.ofFileId(fileId) - ) + fun completionsDataSource(fileId: CreateEvalCompletionsRunDataSource.Source.FileId) = + completionsDataSource(CreateEvalCompletionsRunDataSource.Source.ofFileId(fileId)) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with the following: + * Alias for calling [completionsDataSource] with the following: * ```java * CreateEvalCompletionsRunDataSource.Source.FileId.builder() * .id(id) * .build() * ``` */ - fun fileIdCreateEvalCompletionsRunDataSourceDataSource(id: String) = - createEvalCompletionsRunDataSourceDataSource( + fun fileIdCompletionsDataSource(id: String) = + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.FileId.builder().id(id).build() ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with + * Alias for calling [completionsDataSource] with * `CreateEvalCompletionsRunDataSource.Source.ofStoredCompletions(storedCompletions)`. */ - fun createEvalCompletionsRunDataSourceDataSource( + fun completionsDataSource( storedCompletions: CreateEvalCompletionsRunDataSource.Source.StoredCompletions ) = - createEvalCompletionsRunDataSourceDataSource( + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.ofStoredCompletions(storedCompletions) ) - /** Alias for calling [dataSource] with `DataSource.ofCompletions(completions)`. */ - fun dataSource(completions: DataSource.Completions) = - dataSource(DataSource.ofCompletions(completions)) + /** Alias for calling [dataSource] with `DataSource.ofResponses(responses)`. */ + fun dataSource(responses: DataSource.Responses) = + dataSource(DataSource.ofResponses(responses)) /** * Alias for calling [dataSource] with the following: * ```java - * DataSource.Completions.builder() + * DataSource.Responses.builder() * .source(source) * .build() * ``` */ - fun completionsDataSource(source: DataSource.Completions.Source) = - dataSource(DataSource.Completions.builder().source(source).build()) + fun responsesDataSource(source: DataSource.Responses.Source) = + dataSource(DataSource.Responses.builder().source(source).build()) /** - * Alias for calling [completionsDataSource] with - * `DataSource.Completions.Source.ofFileContent(fileContent)`. + * Alias for calling [responsesDataSource] with + * `DataSource.Responses.Source.ofFileContent(fileContent)`. */ - fun completionsDataSource(fileContent: DataSource.Completions.Source.FileContent) = - completionsDataSource(DataSource.Completions.Source.ofFileContent(fileContent)) + fun responsesDataSource(fileContent: DataSource.Responses.Source.FileContent) = + responsesDataSource(DataSource.Responses.Source.ofFileContent(fileContent)) /** - * Alias for calling [completionsDataSource] with the following: + * Alias for calling [responsesDataSource] with the following: * ```java - * DataSource.Completions.Source.FileContent.builder() + * DataSource.Responses.Source.FileContent.builder() * .content(content) * .build() * ``` */ - fun fileContentCompletionsDataSource( - content: List + fun fileContentResponsesDataSource( + content: List ) = - completionsDataSource( - DataSource.Completions.Source.FileContent.builder().content(content).build() + responsesDataSource( + DataSource.Responses.Source.FileContent.builder().content(content).build() ) /** - * Alias for calling [completionsDataSource] with - * `DataSource.Completions.Source.ofFileId(fileId)`. + * Alias for calling [responsesDataSource] with + * `DataSource.Responses.Source.ofFileId(fileId)`. */ - fun completionsDataSource(fileId: DataSource.Completions.Source.FileId) = - completionsDataSource(DataSource.Completions.Source.ofFileId(fileId)) + fun responsesDataSource(fileId: DataSource.Responses.Source.FileId) = + responsesDataSource(DataSource.Responses.Source.ofFileId(fileId)) /** - * Alias for calling [completionsDataSource] with the following: + * Alias for calling [responsesDataSource] with the following: * ```java - * DataSource.Completions.Source.FileId.builder() + * DataSource.Responses.Source.FileId.builder() * .id(id) * .build() * ``` */ - fun fileIdCompletionsDataSource(id: String) = - completionsDataSource(DataSource.Completions.Source.FileId.builder().id(id).build()) + fun fileIdResponsesDataSource(id: String) = + responsesDataSource(DataSource.Responses.Source.FileId.builder().id(id).build()) /** - * Alias for calling [completionsDataSource] with - * `DataSource.Completions.Source.ofResponses(responses)`. + * Alias for calling [responsesDataSource] with + * `DataSource.Responses.Source.ofResponses(responses)`. */ - fun completionsDataSource(responses: DataSource.Completions.Source.Responses) = - completionsDataSource(DataSource.Completions.Source.ofResponses(responses)) + fun responsesDataSource(responses: DataSource.Responses.Source.InnerResponses) = + responsesDataSource(DataSource.Responses.Source.ofResponses(responses)) /** An object representing an error response from the Eval API. */ fun error(error: EvalApiError) = error(JsonField.of(error)) @@ -942,8 +931,8 @@ private constructor( class DataSource private constructor( private val jsonl: CreateEvalJsonlRunDataSource? = null, - private val createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource? = null, - private val completions: Completions? = null, + private val completions: CreateEvalCompletionsRunDataSource? = null, + private val responses: Responses? = null, private val _json: JsonValue? = null, ) { @@ -951,39 +940,35 @@ private constructor( fun jsonl(): Optional = Optional.ofNullable(jsonl) /** A CompletionsRunDataSource object describing a model sampling configuration. */ - fun createEvalCompletionsRunDataSource(): Optional = - Optional.ofNullable(createEvalCompletionsRunDataSource) + fun completions(): Optional = + Optional.ofNullable(completions) /** A ResponsesRunDataSource object describing a model sampling configuration. */ - fun completions(): Optional = Optional.ofNullable(completions) + fun responses(): Optional = Optional.ofNullable(responses) fun isJsonl(): Boolean = jsonl != null - fun isCreateEvalCompletionsRunDataSource(): Boolean = - createEvalCompletionsRunDataSource != null - fun isCompletions(): Boolean = completions != null + fun isResponses(): Boolean = responses != null + /** A JsonlRunDataSource object with that specifies a JSONL file that matches the eval */ fun asJsonl(): CreateEvalJsonlRunDataSource = jsonl.getOrThrow("jsonl") /** A CompletionsRunDataSource object describing a model sampling configuration. */ - fun asCreateEvalCompletionsRunDataSource(): CreateEvalCompletionsRunDataSource = - createEvalCompletionsRunDataSource.getOrThrow("createEvalCompletionsRunDataSource") + fun asCompletions(): CreateEvalCompletionsRunDataSource = + completions.getOrThrow("completions") /** A ResponsesRunDataSource object describing a model sampling configuration. */ - fun asCompletions(): Completions = completions.getOrThrow("completions") + fun asResponses(): Responses = responses.getOrThrow("responses") fun _json(): Optional = Optional.ofNullable(_json) fun accept(visitor: Visitor): T = when { jsonl != null -> visitor.visitJsonl(jsonl) - createEvalCompletionsRunDataSource != null -> - visitor.visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource - ) completions != null -> visitor.visitCompletions(completions) + responses != null -> visitor.visitResponses(responses) else -> visitor.unknown(_json) } @@ -1000,14 +985,12 @@ private constructor( jsonl.validate() } - override fun visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ) { - createEvalCompletionsRunDataSource.validate() + override fun visitCompletions(completions: CreateEvalCompletionsRunDataSource) { + completions.validate() } - override fun visitCompletions(completions: Completions) { - completions.validate() + override fun visitResponses(responses: Responses) { + responses.validate() } } ) @@ -1034,11 +1017,10 @@ private constructor( object : Visitor { override fun visitJsonl(jsonl: CreateEvalJsonlRunDataSource) = jsonl.validity() - override fun visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ) = createEvalCompletionsRunDataSource.validity() + override fun visitCompletions(completions: CreateEvalCompletionsRunDataSource) = + completions.validity() - override fun visitCompletions(completions: Completions) = completions.validity() + override fun visitResponses(responses: Responses) = responses.validity() override fun unknown(json: JsonValue?) = 0 } @@ -1049,17 +1031,16 @@ private constructor( return true } - return /* spotless:off */ other is DataSource && jsonl == other.jsonl && createEvalCompletionsRunDataSource == other.createEvalCompletionsRunDataSource && completions == other.completions /* spotless:on */ + return /* spotless:off */ other is DataSource && jsonl == other.jsonl && completions == other.completions && responses == other.responses /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(jsonl, createEvalCompletionsRunDataSource, completions) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(jsonl, completions, responses) /* spotless:on */ override fun toString(): String = when { jsonl != null -> "DataSource{jsonl=$jsonl}" - createEvalCompletionsRunDataSource != null -> - "DataSource{createEvalCompletionsRunDataSource=$createEvalCompletionsRunDataSource}" completions != null -> "DataSource{completions=$completions}" + responses != null -> "DataSource{responses=$responses}" _json != null -> "DataSource{_unknown=$_json}" else -> throw IllegalStateException("Invalid DataSource") } @@ -1073,13 +1054,11 @@ private constructor( /** A CompletionsRunDataSource object describing a model sampling configuration. */ @JvmStatic - fun ofCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ) = DataSource(createEvalCompletionsRunDataSource = createEvalCompletionsRunDataSource) + fun ofCompletions(completions: CreateEvalCompletionsRunDataSource) = + DataSource(completions = completions) /** A ResponsesRunDataSource object describing a model sampling configuration. */ - @JvmStatic - fun ofCompletions(completions: Completions) = DataSource(completions = completions) + @JvmStatic fun ofResponses(responses: Responses) = DataSource(responses = responses) } /** @@ -1093,12 +1072,10 @@ private constructor( fun visitJsonl(jsonl: CreateEvalJsonlRunDataSource): T /** A CompletionsRunDataSource object describing a model sampling configuration. */ - fun visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ): T + fun visitCompletions(completions: CreateEvalCompletionsRunDataSource): T /** A ResponsesRunDataSource object describing a model sampling configuration. */ - fun visitCompletions(completions: Completions): T + fun visitResponses(responses: Responses): T /** * Maps an unknown variant of [DataSource] to a value of type [T]. @@ -1128,36 +1105,17 @@ private constructor( ?: DataSource(_json = json) } "completions" -> { - val bestMatches = - sequenceOf( - tryDeserialize( - node, - jacksonTypeRef(), - ) - ?.let { - DataSource( - createEvalCompletionsRunDataSource = it, - _json = json, - ) - }, - tryDeserialize(node, jacksonTypeRef())?.let { - DataSource(completions = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing from - // boolean). - 0 -> DataSource(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then use - // the first completely valid match, or simply the first match if none - // are completely valid. - else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { DataSource(completions = it, _json = json) } + ?: DataSource(_json = json) + } + "responses" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DataSource(responses = it, _json = json) + } ?: DataSource(_json = json) } } @@ -1174,9 +1132,8 @@ private constructor( ) { when { value.jsonl != null -> generator.writeObject(value.jsonl) - value.createEvalCompletionsRunDataSource != null -> - generator.writeObject(value.createEvalCompletionsRunDataSource) value.completions != null -> generator.writeObject(value.completions) + value.responses != null -> generator.writeObject(value.responses) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid DataSource") } @@ -1184,7 +1141,7 @@ private constructor( } /** A ResponsesRunDataSource object describing a model sampling configuration. */ - class Completions + class Responses private constructor( private val source: JsonField, private val type: JsonValue, @@ -1210,7 +1167,7 @@ private constructor( ) : this(source, type, inputMessages, model, samplingParams, mutableMapOf()) /** - * A EvalResponsesSource object describing a run data source configuration. + * Determines what populates the `item` namespace in this run's data source. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected @@ -1219,11 +1176,11 @@ private constructor( fun source(): Source = source.getRequired("source") /** - * The type of run data source. Always `completions`. + * The type of run data source. Always `responses`. * * Expected to always return the following: * ```java - * JsonValue.from("completions") + * JsonValue.from("responses") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1232,6 +1189,11 @@ private constructor( @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** + * Used when sampling from a model. Dictates the structure of the messages passed into + * the model. Can either be a reference to a prebuilt trajectory (ie, + * `item.input_trajectory`), or a template with variable references to the `item` + * namespace. + * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ @@ -1302,7 +1264,7 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [Completions]. + * Returns a mutable builder for constructing an instance of [Responses]. * * The following fields are required: * ```java @@ -1312,27 +1274,27 @@ private constructor( @JvmStatic fun builder() = Builder() } - /** A builder for [Completions]. */ + /** A builder for [Responses]. */ class Builder internal constructor() { private var source: JsonField? = null - private var type: JsonValue = JsonValue.from("completions") + private var type: JsonValue = JsonValue.from("responses") private var inputMessages: JsonField = JsonMissing.of() private var model: JsonField = JsonMissing.of() private var samplingParams: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(completions: Completions) = apply { - source = completions.source - type = completions.type - inputMessages = completions.inputMessages - model = completions.model - samplingParams = completions.samplingParams - additionalProperties = completions.additionalProperties.toMutableMap() + internal fun from(responses: Responses) = apply { + source = responses.source + type = responses.type + inputMessages = responses.inputMessages + model = responses.model + samplingParams = responses.samplingParams + additionalProperties = responses.additionalProperties.toMutableMap() } - /** A EvalResponsesSource object describing a run data source configuration. */ + /** Determines what populates the `item` namespace in this run's data source. */ fun source(source: Source) = source(JsonField.of(source)) /** @@ -1373,7 +1335,7 @@ private constructor( fun fileIdSource(id: String) = source(Source.FileId.builder().id(id).build()) /** Alias for calling [source] with `Source.ofResponses(responses)`. */ - fun source(responses: Source.Responses) = source(Source.ofResponses(responses)) + fun source(responses: Source.InnerResponses) = source(Source.ofResponses(responses)) /** * Sets the field to an arbitrary JSON value. @@ -1381,7 +1343,7 @@ private constructor( * It is usually unnecessary to call this method because the field defaults to the * following: * ```java - * JsonValue.from("completions") + * JsonValue.from("responses") * ``` * * This method is primarily for setting the field to an undocumented or not yet @@ -1389,6 +1351,12 @@ private constructor( */ fun type(type: JsonValue) = apply { this.type = type } + /** + * Used when sampling from a model. Dictates the structure of the messages passed + * into the model. Can either be a reference to a prebuilt trajectory (ie, + * `item.input_trajectory`), or a template with variable references to the `item` + * namespace. + */ fun inputMessages(inputMessages: InputMessages) = inputMessages(JsonField.of(inputMessages)) @@ -1487,7 +1455,7 @@ private constructor( } /** - * Returns an immutable instance of [Completions]. + * Returns an immutable instance of [Responses]. * * Further updates to this [Builder] will not mutate the returned instance. * @@ -1498,8 +1466,8 @@ private constructor( * * @throws IllegalStateException if any required field is unset. */ - fun build(): Completions = - Completions( + fun build(): Responses = + Responses( checkRequired("source", source), type, inputMessages, @@ -1511,14 +1479,14 @@ private constructor( private var validated: Boolean = false - fun validate(): Completions = apply { + fun validate(): Responses = apply { if (validated) { return@apply } source().validate() _type().let { - if (it != JsonValue.from("completions")) { + if (it != JsonValue.from("responses")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") } } @@ -1545,19 +1513,19 @@ private constructor( @JvmSynthetic internal fun validity(): Int = (source.asKnown().getOrNull()?.validity() ?: 0) + - type.let { if (it == JsonValue.from("completions")) 1 else 0 } + + type.let { if (it == JsonValue.from("responses")) 1 else 0 } + (inputMessages.asKnown().getOrNull()?.validity() ?: 0) + (if (model.asKnown().isPresent) 1 else 0) + (samplingParams.asKnown().getOrNull()?.validity() ?: 0) - /** A EvalResponsesSource object describing a run data source configuration. */ + /** Determines what populates the `item` namespace in this run's data source. */ @JsonDeserialize(using = Source.Deserializer::class) @JsonSerialize(using = Source.Serializer::class) class Source private constructor( private val fileContent: FileContent? = null, private val fileId: FileId? = null, - private val responses: Responses? = null, + private val responses: InnerResponses? = null, private val _json: JsonValue? = null, ) { @@ -1566,7 +1534,7 @@ private constructor( fun fileId(): Optional = Optional.ofNullable(fileId) /** A EvalResponsesSource object describing a run data source configuration. */ - fun responses(): Optional = Optional.ofNullable(responses) + fun responses(): Optional = Optional.ofNullable(responses) fun isFileContent(): Boolean = fileContent != null @@ -1579,7 +1547,7 @@ private constructor( fun asFileId(): FileId = fileId.getOrThrow("fileId") /** A EvalResponsesSource object describing a run data source configuration. */ - fun asResponses(): Responses = responses.getOrThrow("responses") + fun asResponses(): InnerResponses = responses.getOrThrow("responses") fun _json(): Optional = Optional.ofNullable(_json) @@ -1608,7 +1576,7 @@ private constructor( fileId.validate() } - override fun visitResponses(responses: Responses) { + override fun visitResponses(responses: InnerResponses) { responses.validate() } } @@ -1639,7 +1607,8 @@ private constructor( override fun visitFileId(fileId: FileId) = fileId.validity() - override fun visitResponses(responses: Responses) = responses.validity() + override fun visitResponses(responses: InnerResponses) = + responses.validity() override fun unknown(json: JsonValue?) = 0 } @@ -1672,7 +1641,8 @@ private constructor( @JvmStatic fun ofFileId(fileId: FileId) = Source(fileId = fileId) /** A EvalResponsesSource object describing a run data source configuration. */ - @JvmStatic fun ofResponses(responses: Responses) = Source(responses = responses) + @JvmStatic + fun ofResponses(responses: InnerResponses) = Source(responses = responses) } /** @@ -1686,7 +1656,7 @@ private constructor( fun visitFileId(fileId: FileId): T /** A EvalResponsesSource object describing a run data source configuration. */ - fun visitResponses(responses: Responses): T + fun visitResponses(responses: InnerResponses): T /** * Maps an unknown variant of [Source] to a value of type [T]. @@ -1721,7 +1691,7 @@ private constructor( } ?: Source(_json = json) } "responses" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { + return tryDeserialize(node, jacksonTypeRef())?.let { Source(responses = it, _json = json) } ?: Source(_json = json) } @@ -2608,18 +2578,17 @@ private constructor( } /** A EvalResponsesSource object describing a run data source configuration. */ - class Responses + class InnerResponses private constructor( private val type: JsonValue, - private val allowParallelToolCalls: JsonField, private val createdAfter: JsonField, private val createdBefore: JsonField, - private val hasToolCalls: JsonField, private val instructionsSearch: JsonField, private val metadata: JsonValue, private val model: JsonField, private val reasoningEffort: JsonField, private val temperature: JsonField, + private val tools: JsonField>, private val topP: JsonField, private val users: JsonField>, private val additionalProperties: MutableMap, @@ -2628,18 +2597,12 @@ private constructor( @JsonCreator private constructor( @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - @JsonProperty("allow_parallel_tool_calls") - @ExcludeMissing - allowParallelToolCalls: JsonField = JsonMissing.of(), @JsonProperty("created_after") @ExcludeMissing createdAfter: JsonField = JsonMissing.of(), @JsonProperty("created_before") @ExcludeMissing createdBefore: JsonField = JsonMissing.of(), - @JsonProperty("has_tool_calls") - @ExcludeMissing - hasToolCalls: JsonField = JsonMissing.of(), @JsonProperty("instructions_search") @ExcludeMissing instructionsSearch: JsonField = JsonMissing.of(), @@ -2655,6 +2618,9 @@ private constructor( @JsonProperty("temperature") @ExcludeMissing temperature: JsonField = JsonMissing.of(), + @JsonProperty("tools") + @ExcludeMissing + tools: JsonField> = JsonMissing.of(), @JsonProperty("top_p") @ExcludeMissing topP: JsonField = JsonMissing.of(), @@ -2663,15 +2629,14 @@ private constructor( users: JsonField> = JsonMissing.of(), ) : this( type, - allowParallelToolCalls, createdAfter, createdBefore, - hasToolCalls, instructionsSearch, metadata, model, reasoningEffort, temperature, + tools, topP, users, mutableMapOf(), @@ -2690,16 +2655,6 @@ private constructor( */ @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - /** - * Whether to allow parallel tool calls. This is a query parameter used to - * select responses. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). - */ - fun allowParallelToolCalls(): Optional = - allowParallelToolCalls.getOptional("allow_parallel_tool_calls") - /** * Only include items created after this timestamp (inclusive). This is a query * parameter used to select responses. @@ -2720,18 +2675,8 @@ private constructor( createdBefore.getOptional("created_before") /** - * Whether the response has tool calls. This is a query parameter used to select - * responses. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). - */ - fun hasToolCalls(): Optional = - hasToolCalls.getOptional("has_tool_calls") - - /** - * Optional search string for instructions. This is a query parameter used to - * select responses. + * Optional string to search the 'instructions' field. This is a query parameter + * used to select responses. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * (e.g. if the server responded with an unexpected value). @@ -2772,6 +2717,14 @@ private constructor( */ fun temperature(): Optional = temperature.getOptional("temperature") + /** + * List of tool names. This is a query parameter used to select responses. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun tools(): Optional> = tools.getOptional("tools") + /** * Nucleus sampling parameter. This is a query parameter used to select * responses. @@ -2789,16 +2742,6 @@ private constructor( */ fun users(): Optional> = users.getOptional("users") - /** - * Returns the raw JSON value of [allowParallelToolCalls]. - * - * Unlike [allowParallelToolCalls], this method doesn't throw if the JSON field - * has an unexpected type. - */ - @JsonProperty("allow_parallel_tool_calls") - @ExcludeMissing - fun _allowParallelToolCalls(): JsonField = allowParallelToolCalls - /** * Returns the raw JSON value of [createdAfter]. * @@ -2819,16 +2762,6 @@ private constructor( @ExcludeMissing fun _createdBefore(): JsonField = createdBefore - /** - * Returns the raw JSON value of [hasToolCalls]. - * - * Unlike [hasToolCalls], this method doesn't throw if the JSON field has an - * unexpected type. - */ - @JsonProperty("has_tool_calls") - @ExcludeMissing - fun _hasToolCalls(): JsonField = hasToolCalls - /** * Returns the raw JSON value of [instructionsSearch]. * @@ -2867,6 +2800,16 @@ private constructor( @ExcludeMissing fun _temperature(): JsonField = temperature + /** + * Returns the raw JSON value of [tools]. + * + * Unlike [tools], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("tools") + @ExcludeMissing + fun _tools(): JsonField> = tools + /** * Returns the raw JSON value of [topP]. * @@ -2900,44 +2843,44 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [Responses]. + * Returns a mutable builder for constructing an instance of + * [InnerResponses]. */ @JvmStatic fun builder() = Builder() } - /** A builder for [Responses]. */ + /** A builder for [InnerResponses]. */ class Builder internal constructor() { private var type: JsonValue = JsonValue.from("responses") - private var allowParallelToolCalls: JsonField = JsonMissing.of() private var createdAfter: JsonField = JsonMissing.of() private var createdBefore: JsonField = JsonMissing.of() - private var hasToolCalls: JsonField = JsonMissing.of() private var instructionsSearch: JsonField = JsonMissing.of() private var metadata: JsonValue = JsonMissing.of() private var model: JsonField = JsonMissing.of() private var reasoningEffort: JsonField = JsonMissing.of() private var temperature: JsonField = JsonMissing.of() + private var tools: JsonField>? = null private var topP: JsonField = JsonMissing.of() private var users: JsonField>? = null private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(responses: Responses) = apply { - type = responses.type - allowParallelToolCalls = responses.allowParallelToolCalls - createdAfter = responses.createdAfter - createdBefore = responses.createdBefore - hasToolCalls = responses.hasToolCalls - instructionsSearch = responses.instructionsSearch - metadata = responses.metadata - model = responses.model - reasoningEffort = responses.reasoningEffort - temperature = responses.temperature - topP = responses.topP - users = responses.users.map { it.toMutableList() } - additionalProperties = responses.additionalProperties.toMutableMap() + internal fun from(innerResponses: InnerResponses) = apply { + type = innerResponses.type + createdAfter = innerResponses.createdAfter + createdBefore = innerResponses.createdBefore + instructionsSearch = innerResponses.instructionsSearch + metadata = innerResponses.metadata + model = innerResponses.model + reasoningEffort = innerResponses.reasoningEffort + temperature = innerResponses.temperature + tools = innerResponses.tools.map { it.toMutableList() } + topP = innerResponses.topP + users = innerResponses.users.map { it.toMutableList() } + additionalProperties = + innerResponses.additionalProperties.toMutableMap() } /** @@ -2954,40 +2897,6 @@ private constructor( */ fun type(type: JsonValue) = apply { this.type = type } - /** - * Whether to allow parallel tool calls. This is a query parameter used to - * select responses. - */ - fun allowParallelToolCalls(allowParallelToolCalls: Boolean?) = - allowParallelToolCalls(JsonField.ofNullable(allowParallelToolCalls)) - - /** - * Alias for [Builder.allowParallelToolCalls]. - * - * This unboxed primitive overload exists for backwards compatibility. - */ - fun allowParallelToolCalls(allowParallelToolCalls: Boolean) = - allowParallelToolCalls(allowParallelToolCalls as Boolean?) - - /** - * Alias for calling [Builder.allowParallelToolCalls] with - * `allowParallelToolCalls.orElse(null)`. - */ - fun allowParallelToolCalls(allowParallelToolCalls: Optional) = - allowParallelToolCalls(allowParallelToolCalls.getOrNull()) - - /** - * Sets [Builder.allowParallelToolCalls] to an arbitrary JSON value. - * - * You should usually call [Builder.allowParallelToolCalls] with a - * well-typed [Boolean] value instead. This method is primarily for setting - * the field to an undocumented or not yet supported value. - */ - fun allowParallelToolCalls(allowParallelToolCalls: JsonField) = - apply { - this.allowParallelToolCalls = allowParallelToolCalls - } - /** * Only include items created after this timestamp (inclusive). This is a * query parameter used to select responses. @@ -3054,41 +2963,8 @@ private constructor( } /** - * Whether the response has tool calls. This is a query parameter used to - * select responses. - */ - fun hasToolCalls(hasToolCalls: Boolean?) = - hasToolCalls(JsonField.ofNullable(hasToolCalls)) - - /** - * Alias for [Builder.hasToolCalls]. - * - * This unboxed primitive overload exists for backwards compatibility. - */ - fun hasToolCalls(hasToolCalls: Boolean) = - hasToolCalls(hasToolCalls as Boolean?) - - /** - * Alias for calling [Builder.hasToolCalls] with - * `hasToolCalls.orElse(null)`. - */ - fun hasToolCalls(hasToolCalls: Optional) = - hasToolCalls(hasToolCalls.getOrNull()) - - /** - * Sets [Builder.hasToolCalls] to an arbitrary JSON value. - * - * You should usually call [Builder.hasToolCalls] with a well-typed - * [Boolean] value instead. This method is primarily for setting the field - * to an undocumented or not yet supported value. - */ - fun hasToolCalls(hasToolCalls: JsonField) = apply { - this.hasToolCalls = hasToolCalls - } - - /** - * Optional search string for instructions. This is a query parameter used - * to select responses. + * Optional string to search the 'instructions' field. This is a query + * parameter used to select responses. */ fun instructionsSearch(instructionsSearch: String?) = instructionsSearch(JsonField.ofNullable(instructionsSearch)) @@ -3190,6 +3066,38 @@ private constructor( this.temperature = temperature } + /** + * List of tool names. This is a query parameter used to select responses. + */ + fun tools(tools: List?) = tools(JsonField.ofNullable(tools)) + + /** Alias for calling [Builder.tools] with `tools.orElse(null)`. */ + fun tools(tools: Optional>) = tools(tools.getOrNull()) + + /** + * Sets [Builder.tools] to an arbitrary JSON value. + * + * You should usually call [Builder.tools] with a well-typed `List` + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun tools(tools: JsonField>) = apply { + this.tools = tools.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [tools]. + * + * @throws IllegalStateException if the field was previously set to a + * non-list. + */ + fun addTool(tool: String) = apply { + tools = + (tools ?: JsonField.of(mutableListOf())).also { + checkKnown("tools", it).add(tool) + } + } + /** * Nucleus sampling parameter. This is a query parameter used to select * responses. @@ -3271,22 +3179,21 @@ private constructor( } /** - * Returns an immutable instance of [Responses]. + * Returns an immutable instance of [InnerResponses]. * * Further updates to this [Builder] will not mutate the returned instance. */ - fun build(): Responses = - Responses( + fun build(): InnerResponses = + InnerResponses( type, - allowParallelToolCalls, createdAfter, createdBefore, - hasToolCalls, instructionsSearch, metadata, model, reasoningEffort, temperature, + (tools ?: JsonMissing.of()).map { it.toImmutable() }, topP, (users ?: JsonMissing.of()).map { it.toImmutable() }, additionalProperties.toMutableMap(), @@ -3295,7 +3202,7 @@ private constructor( private var validated: Boolean = false - fun validate(): Responses = apply { + fun validate(): InnerResponses = apply { if (validated) { return@apply } @@ -3305,14 +3212,13 @@ private constructor( throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - allowParallelToolCalls() createdAfter() createdBefore() - hasToolCalls() instructionsSearch() model() reasoningEffort().ifPresent { it.validate() } temperature() + tools() topP() users() validated = true @@ -3335,14 +3241,13 @@ private constructor( @JvmSynthetic internal fun validity(): Int = type.let { if (it == JsonValue.from("responses")) 1 else 0 } + - (if (allowParallelToolCalls.asKnown().isPresent) 1 else 0) + (if (createdAfter.asKnown().isPresent) 1 else 0) + (if (createdBefore.asKnown().isPresent) 1 else 0) + - (if (hasToolCalls.asKnown().isPresent) 1 else 0) + (if (instructionsSearch.asKnown().isPresent) 1 else 0) + (if (model.asKnown().isPresent) 1 else 0) + (reasoningEffort.asKnown().getOrNull()?.validity() ?: 0) + (if (temperature.asKnown().isPresent) 1 else 0) + + (tools.asKnown().getOrNull()?.size ?: 0) + (if (topP.asKnown().isPresent) 1 else 0) + (users.asKnown().getOrNull()?.size ?: 0) @@ -3351,20 +3256,26 @@ private constructor( return true } - return /* spotless:off */ other is Responses && type == other.type && allowParallelToolCalls == other.allowParallelToolCalls && createdAfter == other.createdAfter && createdBefore == other.createdBefore && hasToolCalls == other.hasToolCalls && instructionsSearch == other.instructionsSearch && metadata == other.metadata && model == other.model && reasoningEffort == other.reasoningEffort && temperature == other.temperature && topP == other.topP && users == other.users && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is InnerResponses && type == other.type && createdAfter == other.createdAfter && createdBefore == other.createdBefore && instructionsSearch == other.instructionsSearch && metadata == other.metadata && model == other.model && reasoningEffort == other.reasoningEffort && temperature == other.temperature && tools == other.tools && topP == other.topP && users == other.users && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(type, allowParallelToolCalls, createdAfter, createdBefore, hasToolCalls, instructionsSearch, metadata, model, reasoningEffort, temperature, topP, users, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(type, createdAfter, createdBefore, instructionsSearch, metadata, model, reasoningEffort, temperature, tools, topP, users, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "Responses{type=$type, allowParallelToolCalls=$allowParallelToolCalls, createdAfter=$createdAfter, createdBefore=$createdBefore, hasToolCalls=$hasToolCalls, instructionsSearch=$instructionsSearch, metadata=$metadata, model=$model, reasoningEffort=$reasoningEffort, temperature=$temperature, topP=$topP, users=$users, additionalProperties=$additionalProperties}" + "InnerResponses{type=$type, createdAfter=$createdAfter, createdBefore=$createdBefore, instructionsSearch=$instructionsSearch, metadata=$metadata, model=$model, reasoningEffort=$reasoningEffort, temperature=$temperature, tools=$tools, topP=$topP, users=$users, additionalProperties=$additionalProperties}" } } + /** + * Used when sampling from a model. Dictates the structure of the messages passed into + * the model. Can either be a reference to a prebuilt trajectory (ie, + * `item.input_trajectory`), or a template with variable references to the `item` + * namespace. + */ @JsonDeserialize(using = InputMessages.Deserializer::class) @JsonSerialize(using = InputMessages.Serializer::class) class InputMessages @@ -3554,7 +3465,7 @@ private constructor( /** * A list of chat messages forming the prompt or context. May include variable - * references to the "item" namespace, ie {{item.name}}. + * references to the `item` namespace, ie {{item.name}}. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * or is unexpectedly missing or null (e.g. if the server responded with an @@ -3627,7 +3538,7 @@ private constructor( /** * A list of chat messages forming the prompt or context. May include - * variable references to the "item" namespace, ie {{item.name}}. + * variable references to the `item` namespace, ie {{item.name}}. */ fun template(template: List) = template(JsonField.of(template)) @@ -5304,7 +5215,7 @@ private constructor( ) : this(itemReference, type, mutableMapOf()) /** - * A reference to a variable in the "item" namespace. Ie, "item.name" + * A reference to a variable in the `item` namespace. Ie, "item.name" * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * or is unexpectedly missing or null (e.g. if the server responded with an @@ -5376,7 +5287,7 @@ private constructor( additionalProperties = itemReference.additionalProperties.toMutableMap() } - /** A reference to a variable in the "item" namespace. Ie, "item.name" */ + /** A reference to a variable in the `item` namespace. Ie, "item.name" */ fun itemReference(itemReference: String) = itemReference(JsonField.of(itemReference)) @@ -5781,7 +5692,7 @@ private constructor( return true } - return /* spotless:off */ other is Completions && source == other.source && type == other.type && inputMessages == other.inputMessages && model == other.model && samplingParams == other.samplingParams && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is Responses && source == other.source && type == other.type && inputMessages == other.inputMessages && model == other.model && samplingParams == other.samplingParams && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ @@ -5791,7 +5702,7 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "Completions{source=$source, type=$type, inputMessages=$inputMessages, model=$model, samplingParams=$samplingParams, additionalProperties=$additionalProperties}" + "Responses{source=$source, type=$type, inputMessages=$inputMessages, model=$model, samplingParams=$samplingParams, additionalProperties=$additionalProperties}" } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunRetrieveParams.kt index b5cfc500..26e388e7 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunRetrieveParams.kt @@ -7,19 +7,21 @@ import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Get an evaluation run by ID. */ class RunRetrieveParams private constructor( private val evalId: String, - private val runId: String, + private val runId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun evalId(): String = evalId - fun runId(): String = runId + fun runId(): Optional = Optional.ofNullable(runId) fun _additionalHeaders(): Headers = additionalHeaders @@ -35,7 +37,6 @@ private constructor( * The following fields are required: * ```java * .evalId() - * .runId() * ``` */ @JvmStatic fun builder() = Builder() @@ -59,7 +60,10 @@ private constructor( fun evalId(evalId: String) = apply { this.evalId = evalId } - fun runId(runId: String) = apply { this.runId = runId } + fun runId(runId: String?) = apply { this.runId = runId } + + /** Alias for calling [Builder.runId] with `runId.orElse(null)`. */ + fun runId(runId: Optional) = runId(runId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -167,7 +171,6 @@ private constructor( * The following fields are required: * ```java * .evalId() - * .runId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -175,7 +178,7 @@ private constructor( fun build(): RunRetrieveParams = RunRetrieveParams( checkRequired("evalId", evalId), - checkRequired("runId", runId), + runId, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -184,7 +187,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> evalId - 1 -> runId + 1 -> runId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunRetrieveResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunRetrieveResponse.kt index 0d4442c2..e1b77b8c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunRetrieveResponse.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunRetrieveResponse.kt @@ -484,14 +484,9 @@ private constructor( fun fileIdJsonlDataSource(id: String) = jsonlDataSource(CreateEvalJsonlRunDataSource.Source.FileId.builder().id(id).build()) - /** - * Alias for calling [dataSource] with - * `DataSource.ofCreateEvalCompletionsRunDataSource(createEvalCompletionsRunDataSource)`. - */ - fun dataSource(createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource) = - dataSource( - DataSource.ofCreateEvalCompletionsRunDataSource(createEvalCompletionsRunDataSource) - ) + /** Alias for calling [dataSource] with `DataSource.ofCompletions(completions)`. */ + fun dataSource(completions: CreateEvalCompletionsRunDataSource) = + dataSource(DataSource.ofCompletions(completions)) /** * Alias for calling [dataSource] with the following: @@ -502,9 +497,7 @@ private constructor( * .build() * ``` */ - fun createEvalCompletionsRunDataSourceDataSource( - source: CreateEvalCompletionsRunDataSource.Source - ) = + fun completionsDataSource(source: CreateEvalCompletionsRunDataSource.Source) = dataSource( CreateEvalCompletionsRunDataSource.builder() .type(CreateEvalCompletionsRunDataSource.Type.COMPLETIONS) @@ -513,129 +506,125 @@ private constructor( ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with + * Alias for calling [completionsDataSource] with * `CreateEvalCompletionsRunDataSource.Source.ofFileContent(fileContent)`. */ - fun createEvalCompletionsRunDataSourceDataSource( + fun completionsDataSource( fileContent: CreateEvalCompletionsRunDataSource.Source.FileContent ) = - createEvalCompletionsRunDataSourceDataSource( + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.ofFileContent(fileContent) ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with the following: + * Alias for calling [completionsDataSource] with the following: * ```java * CreateEvalCompletionsRunDataSource.Source.FileContent.builder() * .content(content) * .build() * ``` */ - fun fileContentCreateEvalCompletionsRunDataSourceDataSource( + fun fileContentCompletionsDataSource( content: List ) = - createEvalCompletionsRunDataSourceDataSource( + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.FileContent.builder() .content(content) .build() ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with + * Alias for calling [completionsDataSource] with * `CreateEvalCompletionsRunDataSource.Source.ofFileId(fileId)`. */ - fun createEvalCompletionsRunDataSourceDataSource( - fileId: CreateEvalCompletionsRunDataSource.Source.FileId - ) = - createEvalCompletionsRunDataSourceDataSource( - CreateEvalCompletionsRunDataSource.Source.ofFileId(fileId) - ) + fun completionsDataSource(fileId: CreateEvalCompletionsRunDataSource.Source.FileId) = + completionsDataSource(CreateEvalCompletionsRunDataSource.Source.ofFileId(fileId)) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with the following: + * Alias for calling [completionsDataSource] with the following: * ```java * CreateEvalCompletionsRunDataSource.Source.FileId.builder() * .id(id) * .build() * ``` */ - fun fileIdCreateEvalCompletionsRunDataSourceDataSource(id: String) = - createEvalCompletionsRunDataSourceDataSource( + fun fileIdCompletionsDataSource(id: String) = + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.FileId.builder().id(id).build() ) /** - * Alias for calling [createEvalCompletionsRunDataSourceDataSource] with + * Alias for calling [completionsDataSource] with * `CreateEvalCompletionsRunDataSource.Source.ofStoredCompletions(storedCompletions)`. */ - fun createEvalCompletionsRunDataSourceDataSource( + fun completionsDataSource( storedCompletions: CreateEvalCompletionsRunDataSource.Source.StoredCompletions ) = - createEvalCompletionsRunDataSourceDataSource( + completionsDataSource( CreateEvalCompletionsRunDataSource.Source.ofStoredCompletions(storedCompletions) ) - /** Alias for calling [dataSource] with `DataSource.ofCompletions(completions)`. */ - fun dataSource(completions: DataSource.Completions) = - dataSource(DataSource.ofCompletions(completions)) + /** Alias for calling [dataSource] with `DataSource.ofResponses(responses)`. */ + fun dataSource(responses: DataSource.Responses) = + dataSource(DataSource.ofResponses(responses)) /** * Alias for calling [dataSource] with the following: * ```java - * DataSource.Completions.builder() + * DataSource.Responses.builder() * .source(source) * .build() * ``` */ - fun completionsDataSource(source: DataSource.Completions.Source) = - dataSource(DataSource.Completions.builder().source(source).build()) + fun responsesDataSource(source: DataSource.Responses.Source) = + dataSource(DataSource.Responses.builder().source(source).build()) /** - * Alias for calling [completionsDataSource] with - * `DataSource.Completions.Source.ofFileContent(fileContent)`. + * Alias for calling [responsesDataSource] with + * `DataSource.Responses.Source.ofFileContent(fileContent)`. */ - fun completionsDataSource(fileContent: DataSource.Completions.Source.FileContent) = - completionsDataSource(DataSource.Completions.Source.ofFileContent(fileContent)) + fun responsesDataSource(fileContent: DataSource.Responses.Source.FileContent) = + responsesDataSource(DataSource.Responses.Source.ofFileContent(fileContent)) /** - * Alias for calling [completionsDataSource] with the following: + * Alias for calling [responsesDataSource] with the following: * ```java - * DataSource.Completions.Source.FileContent.builder() + * DataSource.Responses.Source.FileContent.builder() * .content(content) * .build() * ``` */ - fun fileContentCompletionsDataSource( - content: List + fun fileContentResponsesDataSource( + content: List ) = - completionsDataSource( - DataSource.Completions.Source.FileContent.builder().content(content).build() + responsesDataSource( + DataSource.Responses.Source.FileContent.builder().content(content).build() ) /** - * Alias for calling [completionsDataSource] with - * `DataSource.Completions.Source.ofFileId(fileId)`. + * Alias for calling [responsesDataSource] with + * `DataSource.Responses.Source.ofFileId(fileId)`. */ - fun completionsDataSource(fileId: DataSource.Completions.Source.FileId) = - completionsDataSource(DataSource.Completions.Source.ofFileId(fileId)) + fun responsesDataSource(fileId: DataSource.Responses.Source.FileId) = + responsesDataSource(DataSource.Responses.Source.ofFileId(fileId)) /** - * Alias for calling [completionsDataSource] with the following: + * Alias for calling [responsesDataSource] with the following: * ```java - * DataSource.Completions.Source.FileId.builder() + * DataSource.Responses.Source.FileId.builder() * .id(id) * .build() * ``` */ - fun fileIdCompletionsDataSource(id: String) = - completionsDataSource(DataSource.Completions.Source.FileId.builder().id(id).build()) + fun fileIdResponsesDataSource(id: String) = + responsesDataSource(DataSource.Responses.Source.FileId.builder().id(id).build()) /** - * Alias for calling [completionsDataSource] with - * `DataSource.Completions.Source.ofResponses(responses)`. + * Alias for calling [responsesDataSource] with + * `DataSource.Responses.Source.ofResponses(responses)`. */ - fun completionsDataSource(responses: DataSource.Completions.Source.Responses) = - completionsDataSource(DataSource.Completions.Source.ofResponses(responses)) + fun responsesDataSource(responses: DataSource.Responses.Source.InnerResponses) = + responsesDataSource(DataSource.Responses.Source.ofResponses(responses)) /** An object representing an error response from the Eval API. */ fun error(error: EvalApiError) = error(JsonField.of(error)) @@ -942,8 +931,8 @@ private constructor( class DataSource private constructor( private val jsonl: CreateEvalJsonlRunDataSource? = null, - private val createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource? = null, - private val completions: Completions? = null, + private val completions: CreateEvalCompletionsRunDataSource? = null, + private val responses: Responses? = null, private val _json: JsonValue? = null, ) { @@ -951,39 +940,35 @@ private constructor( fun jsonl(): Optional = Optional.ofNullable(jsonl) /** A CompletionsRunDataSource object describing a model sampling configuration. */ - fun createEvalCompletionsRunDataSource(): Optional = - Optional.ofNullable(createEvalCompletionsRunDataSource) + fun completions(): Optional = + Optional.ofNullable(completions) /** A ResponsesRunDataSource object describing a model sampling configuration. */ - fun completions(): Optional = Optional.ofNullable(completions) + fun responses(): Optional = Optional.ofNullable(responses) fun isJsonl(): Boolean = jsonl != null - fun isCreateEvalCompletionsRunDataSource(): Boolean = - createEvalCompletionsRunDataSource != null - fun isCompletions(): Boolean = completions != null + fun isResponses(): Boolean = responses != null + /** A JsonlRunDataSource object with that specifies a JSONL file that matches the eval */ fun asJsonl(): CreateEvalJsonlRunDataSource = jsonl.getOrThrow("jsonl") /** A CompletionsRunDataSource object describing a model sampling configuration. */ - fun asCreateEvalCompletionsRunDataSource(): CreateEvalCompletionsRunDataSource = - createEvalCompletionsRunDataSource.getOrThrow("createEvalCompletionsRunDataSource") + fun asCompletions(): CreateEvalCompletionsRunDataSource = + completions.getOrThrow("completions") /** A ResponsesRunDataSource object describing a model sampling configuration. */ - fun asCompletions(): Completions = completions.getOrThrow("completions") + fun asResponses(): Responses = responses.getOrThrow("responses") fun _json(): Optional = Optional.ofNullable(_json) fun accept(visitor: Visitor): T = when { jsonl != null -> visitor.visitJsonl(jsonl) - createEvalCompletionsRunDataSource != null -> - visitor.visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource - ) completions != null -> visitor.visitCompletions(completions) + responses != null -> visitor.visitResponses(responses) else -> visitor.unknown(_json) } @@ -1000,14 +985,12 @@ private constructor( jsonl.validate() } - override fun visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ) { - createEvalCompletionsRunDataSource.validate() + override fun visitCompletions(completions: CreateEvalCompletionsRunDataSource) { + completions.validate() } - override fun visitCompletions(completions: Completions) { - completions.validate() + override fun visitResponses(responses: Responses) { + responses.validate() } } ) @@ -1034,11 +1017,10 @@ private constructor( object : Visitor { override fun visitJsonl(jsonl: CreateEvalJsonlRunDataSource) = jsonl.validity() - override fun visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ) = createEvalCompletionsRunDataSource.validity() + override fun visitCompletions(completions: CreateEvalCompletionsRunDataSource) = + completions.validity() - override fun visitCompletions(completions: Completions) = completions.validity() + override fun visitResponses(responses: Responses) = responses.validity() override fun unknown(json: JsonValue?) = 0 } @@ -1049,17 +1031,16 @@ private constructor( return true } - return /* spotless:off */ other is DataSource && jsonl == other.jsonl && createEvalCompletionsRunDataSource == other.createEvalCompletionsRunDataSource && completions == other.completions /* spotless:on */ + return /* spotless:off */ other is DataSource && jsonl == other.jsonl && completions == other.completions && responses == other.responses /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(jsonl, createEvalCompletionsRunDataSource, completions) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(jsonl, completions, responses) /* spotless:on */ override fun toString(): String = when { jsonl != null -> "DataSource{jsonl=$jsonl}" - createEvalCompletionsRunDataSource != null -> - "DataSource{createEvalCompletionsRunDataSource=$createEvalCompletionsRunDataSource}" completions != null -> "DataSource{completions=$completions}" + responses != null -> "DataSource{responses=$responses}" _json != null -> "DataSource{_unknown=$_json}" else -> throw IllegalStateException("Invalid DataSource") } @@ -1073,13 +1054,11 @@ private constructor( /** A CompletionsRunDataSource object describing a model sampling configuration. */ @JvmStatic - fun ofCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ) = DataSource(createEvalCompletionsRunDataSource = createEvalCompletionsRunDataSource) + fun ofCompletions(completions: CreateEvalCompletionsRunDataSource) = + DataSource(completions = completions) /** A ResponsesRunDataSource object describing a model sampling configuration. */ - @JvmStatic - fun ofCompletions(completions: Completions) = DataSource(completions = completions) + @JvmStatic fun ofResponses(responses: Responses) = DataSource(responses = responses) } /** @@ -1093,12 +1072,10 @@ private constructor( fun visitJsonl(jsonl: CreateEvalJsonlRunDataSource): T /** A CompletionsRunDataSource object describing a model sampling configuration. */ - fun visitCreateEvalCompletionsRunDataSource( - createEvalCompletionsRunDataSource: CreateEvalCompletionsRunDataSource - ): T + fun visitCompletions(completions: CreateEvalCompletionsRunDataSource): T /** A ResponsesRunDataSource object describing a model sampling configuration. */ - fun visitCompletions(completions: Completions): T + fun visitResponses(responses: Responses): T /** * Maps an unknown variant of [DataSource] to a value of type [T]. @@ -1128,36 +1105,17 @@ private constructor( ?: DataSource(_json = json) } "completions" -> { - val bestMatches = - sequenceOf( - tryDeserialize( - node, - jacksonTypeRef(), - ) - ?.let { - DataSource( - createEvalCompletionsRunDataSource = it, - _json = json, - ) - }, - tryDeserialize(node, jacksonTypeRef())?.let { - DataSource(completions = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing from - // boolean). - 0 -> DataSource(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then use - // the first completely valid match, or simply the first match if none - // are completely valid. - else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } + return tryDeserialize( + node, + jacksonTypeRef(), + ) + ?.let { DataSource(completions = it, _json = json) } + ?: DataSource(_json = json) + } + "responses" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DataSource(responses = it, _json = json) + } ?: DataSource(_json = json) } } @@ -1174,9 +1132,8 @@ private constructor( ) { when { value.jsonl != null -> generator.writeObject(value.jsonl) - value.createEvalCompletionsRunDataSource != null -> - generator.writeObject(value.createEvalCompletionsRunDataSource) value.completions != null -> generator.writeObject(value.completions) + value.responses != null -> generator.writeObject(value.responses) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid DataSource") } @@ -1184,7 +1141,7 @@ private constructor( } /** A ResponsesRunDataSource object describing a model sampling configuration. */ - class Completions + class Responses private constructor( private val source: JsonField, private val type: JsonValue, @@ -1210,7 +1167,7 @@ private constructor( ) : this(source, type, inputMessages, model, samplingParams, mutableMapOf()) /** - * A EvalResponsesSource object describing a run data source configuration. + * Determines what populates the `item` namespace in this run's data source. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected @@ -1219,11 +1176,11 @@ private constructor( fun source(): Source = source.getRequired("source") /** - * The type of run data source. Always `completions`. + * The type of run data source. Always `responses`. * * Expected to always return the following: * ```java - * JsonValue.from("completions") + * JsonValue.from("responses") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1232,6 +1189,11 @@ private constructor( @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** + * Used when sampling from a model. Dictates the structure of the messages passed into + * the model. Can either be a reference to a prebuilt trajectory (ie, + * `item.input_trajectory`), or a template with variable references to the `item` + * namespace. + * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ @@ -1302,7 +1264,7 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [Completions]. + * Returns a mutable builder for constructing an instance of [Responses]. * * The following fields are required: * ```java @@ -1312,27 +1274,27 @@ private constructor( @JvmStatic fun builder() = Builder() } - /** A builder for [Completions]. */ + /** A builder for [Responses]. */ class Builder internal constructor() { private var source: JsonField? = null - private var type: JsonValue = JsonValue.from("completions") + private var type: JsonValue = JsonValue.from("responses") private var inputMessages: JsonField = JsonMissing.of() private var model: JsonField = JsonMissing.of() private var samplingParams: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(completions: Completions) = apply { - source = completions.source - type = completions.type - inputMessages = completions.inputMessages - model = completions.model - samplingParams = completions.samplingParams - additionalProperties = completions.additionalProperties.toMutableMap() + internal fun from(responses: Responses) = apply { + source = responses.source + type = responses.type + inputMessages = responses.inputMessages + model = responses.model + samplingParams = responses.samplingParams + additionalProperties = responses.additionalProperties.toMutableMap() } - /** A EvalResponsesSource object describing a run data source configuration. */ + /** Determines what populates the `item` namespace in this run's data source. */ fun source(source: Source) = source(JsonField.of(source)) /** @@ -1373,7 +1335,7 @@ private constructor( fun fileIdSource(id: String) = source(Source.FileId.builder().id(id).build()) /** Alias for calling [source] with `Source.ofResponses(responses)`. */ - fun source(responses: Source.Responses) = source(Source.ofResponses(responses)) + fun source(responses: Source.InnerResponses) = source(Source.ofResponses(responses)) /** * Sets the field to an arbitrary JSON value. @@ -1381,7 +1343,7 @@ private constructor( * It is usually unnecessary to call this method because the field defaults to the * following: * ```java - * JsonValue.from("completions") + * JsonValue.from("responses") * ``` * * This method is primarily for setting the field to an undocumented or not yet @@ -1389,6 +1351,12 @@ private constructor( */ fun type(type: JsonValue) = apply { this.type = type } + /** + * Used when sampling from a model. Dictates the structure of the messages passed + * into the model. Can either be a reference to a prebuilt trajectory (ie, + * `item.input_trajectory`), or a template with variable references to the `item` + * namespace. + */ fun inputMessages(inputMessages: InputMessages) = inputMessages(JsonField.of(inputMessages)) @@ -1487,7 +1455,7 @@ private constructor( } /** - * Returns an immutable instance of [Completions]. + * Returns an immutable instance of [Responses]. * * Further updates to this [Builder] will not mutate the returned instance. * @@ -1498,8 +1466,8 @@ private constructor( * * @throws IllegalStateException if any required field is unset. */ - fun build(): Completions = - Completions( + fun build(): Responses = + Responses( checkRequired("source", source), type, inputMessages, @@ -1511,14 +1479,14 @@ private constructor( private var validated: Boolean = false - fun validate(): Completions = apply { + fun validate(): Responses = apply { if (validated) { return@apply } source().validate() _type().let { - if (it != JsonValue.from("completions")) { + if (it != JsonValue.from("responses")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") } } @@ -1545,19 +1513,19 @@ private constructor( @JvmSynthetic internal fun validity(): Int = (source.asKnown().getOrNull()?.validity() ?: 0) + - type.let { if (it == JsonValue.from("completions")) 1 else 0 } + + type.let { if (it == JsonValue.from("responses")) 1 else 0 } + (inputMessages.asKnown().getOrNull()?.validity() ?: 0) + (if (model.asKnown().isPresent) 1 else 0) + (samplingParams.asKnown().getOrNull()?.validity() ?: 0) - /** A EvalResponsesSource object describing a run data source configuration. */ + /** Determines what populates the `item` namespace in this run's data source. */ @JsonDeserialize(using = Source.Deserializer::class) @JsonSerialize(using = Source.Serializer::class) class Source private constructor( private val fileContent: FileContent? = null, private val fileId: FileId? = null, - private val responses: Responses? = null, + private val responses: InnerResponses? = null, private val _json: JsonValue? = null, ) { @@ -1566,7 +1534,7 @@ private constructor( fun fileId(): Optional = Optional.ofNullable(fileId) /** A EvalResponsesSource object describing a run data source configuration. */ - fun responses(): Optional = Optional.ofNullable(responses) + fun responses(): Optional = Optional.ofNullable(responses) fun isFileContent(): Boolean = fileContent != null @@ -1579,7 +1547,7 @@ private constructor( fun asFileId(): FileId = fileId.getOrThrow("fileId") /** A EvalResponsesSource object describing a run data source configuration. */ - fun asResponses(): Responses = responses.getOrThrow("responses") + fun asResponses(): InnerResponses = responses.getOrThrow("responses") fun _json(): Optional = Optional.ofNullable(_json) @@ -1608,7 +1576,7 @@ private constructor( fileId.validate() } - override fun visitResponses(responses: Responses) { + override fun visitResponses(responses: InnerResponses) { responses.validate() } } @@ -1639,7 +1607,8 @@ private constructor( override fun visitFileId(fileId: FileId) = fileId.validity() - override fun visitResponses(responses: Responses) = responses.validity() + override fun visitResponses(responses: InnerResponses) = + responses.validity() override fun unknown(json: JsonValue?) = 0 } @@ -1672,7 +1641,8 @@ private constructor( @JvmStatic fun ofFileId(fileId: FileId) = Source(fileId = fileId) /** A EvalResponsesSource object describing a run data source configuration. */ - @JvmStatic fun ofResponses(responses: Responses) = Source(responses = responses) + @JvmStatic + fun ofResponses(responses: InnerResponses) = Source(responses = responses) } /** @@ -1686,7 +1656,7 @@ private constructor( fun visitFileId(fileId: FileId): T /** A EvalResponsesSource object describing a run data source configuration. */ - fun visitResponses(responses: Responses): T + fun visitResponses(responses: InnerResponses): T /** * Maps an unknown variant of [Source] to a value of type [T]. @@ -1721,7 +1691,7 @@ private constructor( } ?: Source(_json = json) } "responses" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { + return tryDeserialize(node, jacksonTypeRef())?.let { Source(responses = it, _json = json) } ?: Source(_json = json) } @@ -2608,18 +2578,17 @@ private constructor( } /** A EvalResponsesSource object describing a run data source configuration. */ - class Responses + class InnerResponses private constructor( private val type: JsonValue, - private val allowParallelToolCalls: JsonField, private val createdAfter: JsonField, private val createdBefore: JsonField, - private val hasToolCalls: JsonField, private val instructionsSearch: JsonField, private val metadata: JsonValue, private val model: JsonField, private val reasoningEffort: JsonField, private val temperature: JsonField, + private val tools: JsonField>, private val topP: JsonField, private val users: JsonField>, private val additionalProperties: MutableMap, @@ -2628,18 +2597,12 @@ private constructor( @JsonCreator private constructor( @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - @JsonProperty("allow_parallel_tool_calls") - @ExcludeMissing - allowParallelToolCalls: JsonField = JsonMissing.of(), @JsonProperty("created_after") @ExcludeMissing createdAfter: JsonField = JsonMissing.of(), @JsonProperty("created_before") @ExcludeMissing createdBefore: JsonField = JsonMissing.of(), - @JsonProperty("has_tool_calls") - @ExcludeMissing - hasToolCalls: JsonField = JsonMissing.of(), @JsonProperty("instructions_search") @ExcludeMissing instructionsSearch: JsonField = JsonMissing.of(), @@ -2655,6 +2618,9 @@ private constructor( @JsonProperty("temperature") @ExcludeMissing temperature: JsonField = JsonMissing.of(), + @JsonProperty("tools") + @ExcludeMissing + tools: JsonField> = JsonMissing.of(), @JsonProperty("top_p") @ExcludeMissing topP: JsonField = JsonMissing.of(), @@ -2663,15 +2629,14 @@ private constructor( users: JsonField> = JsonMissing.of(), ) : this( type, - allowParallelToolCalls, createdAfter, createdBefore, - hasToolCalls, instructionsSearch, metadata, model, reasoningEffort, temperature, + tools, topP, users, mutableMapOf(), @@ -2690,16 +2655,6 @@ private constructor( */ @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - /** - * Whether to allow parallel tool calls. This is a query parameter used to - * select responses. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). - */ - fun allowParallelToolCalls(): Optional = - allowParallelToolCalls.getOptional("allow_parallel_tool_calls") - /** * Only include items created after this timestamp (inclusive). This is a query * parameter used to select responses. @@ -2720,18 +2675,8 @@ private constructor( createdBefore.getOptional("created_before") /** - * Whether the response has tool calls. This is a query parameter used to select - * responses. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type - * (e.g. if the server responded with an unexpected value). - */ - fun hasToolCalls(): Optional = - hasToolCalls.getOptional("has_tool_calls") - - /** - * Optional search string for instructions. This is a query parameter used to - * select responses. + * Optional string to search the 'instructions' field. This is a query parameter + * used to select responses. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * (e.g. if the server responded with an unexpected value). @@ -2772,6 +2717,14 @@ private constructor( */ fun temperature(): Optional = temperature.getOptional("temperature") + /** + * List of tool names. This is a query parameter used to select responses. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun tools(): Optional> = tools.getOptional("tools") + /** * Nucleus sampling parameter. This is a query parameter used to select * responses. @@ -2789,16 +2742,6 @@ private constructor( */ fun users(): Optional> = users.getOptional("users") - /** - * Returns the raw JSON value of [allowParallelToolCalls]. - * - * Unlike [allowParallelToolCalls], this method doesn't throw if the JSON field - * has an unexpected type. - */ - @JsonProperty("allow_parallel_tool_calls") - @ExcludeMissing - fun _allowParallelToolCalls(): JsonField = allowParallelToolCalls - /** * Returns the raw JSON value of [createdAfter]. * @@ -2819,16 +2762,6 @@ private constructor( @ExcludeMissing fun _createdBefore(): JsonField = createdBefore - /** - * Returns the raw JSON value of [hasToolCalls]. - * - * Unlike [hasToolCalls], this method doesn't throw if the JSON field has an - * unexpected type. - */ - @JsonProperty("has_tool_calls") - @ExcludeMissing - fun _hasToolCalls(): JsonField = hasToolCalls - /** * Returns the raw JSON value of [instructionsSearch]. * @@ -2867,6 +2800,16 @@ private constructor( @ExcludeMissing fun _temperature(): JsonField = temperature + /** + * Returns the raw JSON value of [tools]. + * + * Unlike [tools], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("tools") + @ExcludeMissing + fun _tools(): JsonField> = tools + /** * Returns the raw JSON value of [topP]. * @@ -2900,44 +2843,44 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [Responses]. + * Returns a mutable builder for constructing an instance of + * [InnerResponses]. */ @JvmStatic fun builder() = Builder() } - /** A builder for [Responses]. */ + /** A builder for [InnerResponses]. */ class Builder internal constructor() { private var type: JsonValue = JsonValue.from("responses") - private var allowParallelToolCalls: JsonField = JsonMissing.of() private var createdAfter: JsonField = JsonMissing.of() private var createdBefore: JsonField = JsonMissing.of() - private var hasToolCalls: JsonField = JsonMissing.of() private var instructionsSearch: JsonField = JsonMissing.of() private var metadata: JsonValue = JsonMissing.of() private var model: JsonField = JsonMissing.of() private var reasoningEffort: JsonField = JsonMissing.of() private var temperature: JsonField = JsonMissing.of() + private var tools: JsonField>? = null private var topP: JsonField = JsonMissing.of() private var users: JsonField>? = null private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(responses: Responses) = apply { - type = responses.type - allowParallelToolCalls = responses.allowParallelToolCalls - createdAfter = responses.createdAfter - createdBefore = responses.createdBefore - hasToolCalls = responses.hasToolCalls - instructionsSearch = responses.instructionsSearch - metadata = responses.metadata - model = responses.model - reasoningEffort = responses.reasoningEffort - temperature = responses.temperature - topP = responses.topP - users = responses.users.map { it.toMutableList() } - additionalProperties = responses.additionalProperties.toMutableMap() + internal fun from(innerResponses: InnerResponses) = apply { + type = innerResponses.type + createdAfter = innerResponses.createdAfter + createdBefore = innerResponses.createdBefore + instructionsSearch = innerResponses.instructionsSearch + metadata = innerResponses.metadata + model = innerResponses.model + reasoningEffort = innerResponses.reasoningEffort + temperature = innerResponses.temperature + tools = innerResponses.tools.map { it.toMutableList() } + topP = innerResponses.topP + users = innerResponses.users.map { it.toMutableList() } + additionalProperties = + innerResponses.additionalProperties.toMutableMap() } /** @@ -2954,40 +2897,6 @@ private constructor( */ fun type(type: JsonValue) = apply { this.type = type } - /** - * Whether to allow parallel tool calls. This is a query parameter used to - * select responses. - */ - fun allowParallelToolCalls(allowParallelToolCalls: Boolean?) = - allowParallelToolCalls(JsonField.ofNullable(allowParallelToolCalls)) - - /** - * Alias for [Builder.allowParallelToolCalls]. - * - * This unboxed primitive overload exists for backwards compatibility. - */ - fun allowParallelToolCalls(allowParallelToolCalls: Boolean) = - allowParallelToolCalls(allowParallelToolCalls as Boolean?) - - /** - * Alias for calling [Builder.allowParallelToolCalls] with - * `allowParallelToolCalls.orElse(null)`. - */ - fun allowParallelToolCalls(allowParallelToolCalls: Optional) = - allowParallelToolCalls(allowParallelToolCalls.getOrNull()) - - /** - * Sets [Builder.allowParallelToolCalls] to an arbitrary JSON value. - * - * You should usually call [Builder.allowParallelToolCalls] with a - * well-typed [Boolean] value instead. This method is primarily for setting - * the field to an undocumented or not yet supported value. - */ - fun allowParallelToolCalls(allowParallelToolCalls: JsonField) = - apply { - this.allowParallelToolCalls = allowParallelToolCalls - } - /** * Only include items created after this timestamp (inclusive). This is a * query parameter used to select responses. @@ -3054,41 +2963,8 @@ private constructor( } /** - * Whether the response has tool calls. This is a query parameter used to - * select responses. - */ - fun hasToolCalls(hasToolCalls: Boolean?) = - hasToolCalls(JsonField.ofNullable(hasToolCalls)) - - /** - * Alias for [Builder.hasToolCalls]. - * - * This unboxed primitive overload exists for backwards compatibility. - */ - fun hasToolCalls(hasToolCalls: Boolean) = - hasToolCalls(hasToolCalls as Boolean?) - - /** - * Alias for calling [Builder.hasToolCalls] with - * `hasToolCalls.orElse(null)`. - */ - fun hasToolCalls(hasToolCalls: Optional) = - hasToolCalls(hasToolCalls.getOrNull()) - - /** - * Sets [Builder.hasToolCalls] to an arbitrary JSON value. - * - * You should usually call [Builder.hasToolCalls] with a well-typed - * [Boolean] value instead. This method is primarily for setting the field - * to an undocumented or not yet supported value. - */ - fun hasToolCalls(hasToolCalls: JsonField) = apply { - this.hasToolCalls = hasToolCalls - } - - /** - * Optional search string for instructions. This is a query parameter used - * to select responses. + * Optional string to search the 'instructions' field. This is a query + * parameter used to select responses. */ fun instructionsSearch(instructionsSearch: String?) = instructionsSearch(JsonField.ofNullable(instructionsSearch)) @@ -3190,6 +3066,38 @@ private constructor( this.temperature = temperature } + /** + * List of tool names. This is a query parameter used to select responses. + */ + fun tools(tools: List?) = tools(JsonField.ofNullable(tools)) + + /** Alias for calling [Builder.tools] with `tools.orElse(null)`. */ + fun tools(tools: Optional>) = tools(tools.getOrNull()) + + /** + * Sets [Builder.tools] to an arbitrary JSON value. + * + * You should usually call [Builder.tools] with a well-typed `List` + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun tools(tools: JsonField>) = apply { + this.tools = tools.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [tools]. + * + * @throws IllegalStateException if the field was previously set to a + * non-list. + */ + fun addTool(tool: String) = apply { + tools = + (tools ?: JsonField.of(mutableListOf())).also { + checkKnown("tools", it).add(tool) + } + } + /** * Nucleus sampling parameter. This is a query parameter used to select * responses. @@ -3271,22 +3179,21 @@ private constructor( } /** - * Returns an immutable instance of [Responses]. + * Returns an immutable instance of [InnerResponses]. * * Further updates to this [Builder] will not mutate the returned instance. */ - fun build(): Responses = - Responses( + fun build(): InnerResponses = + InnerResponses( type, - allowParallelToolCalls, createdAfter, createdBefore, - hasToolCalls, instructionsSearch, metadata, model, reasoningEffort, temperature, + (tools ?: JsonMissing.of()).map { it.toImmutable() }, topP, (users ?: JsonMissing.of()).map { it.toImmutable() }, additionalProperties.toMutableMap(), @@ -3295,7 +3202,7 @@ private constructor( private var validated: Boolean = false - fun validate(): Responses = apply { + fun validate(): InnerResponses = apply { if (validated) { return@apply } @@ -3305,14 +3212,13 @@ private constructor( throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - allowParallelToolCalls() createdAfter() createdBefore() - hasToolCalls() instructionsSearch() model() reasoningEffort().ifPresent { it.validate() } temperature() + tools() topP() users() validated = true @@ -3335,14 +3241,13 @@ private constructor( @JvmSynthetic internal fun validity(): Int = type.let { if (it == JsonValue.from("responses")) 1 else 0 } + - (if (allowParallelToolCalls.asKnown().isPresent) 1 else 0) + (if (createdAfter.asKnown().isPresent) 1 else 0) + (if (createdBefore.asKnown().isPresent) 1 else 0) + - (if (hasToolCalls.asKnown().isPresent) 1 else 0) + (if (instructionsSearch.asKnown().isPresent) 1 else 0) + (if (model.asKnown().isPresent) 1 else 0) + (reasoningEffort.asKnown().getOrNull()?.validity() ?: 0) + (if (temperature.asKnown().isPresent) 1 else 0) + + (tools.asKnown().getOrNull()?.size ?: 0) + (if (topP.asKnown().isPresent) 1 else 0) + (users.asKnown().getOrNull()?.size ?: 0) @@ -3351,20 +3256,26 @@ private constructor( return true } - return /* spotless:off */ other is Responses && type == other.type && allowParallelToolCalls == other.allowParallelToolCalls && createdAfter == other.createdAfter && createdBefore == other.createdBefore && hasToolCalls == other.hasToolCalls && instructionsSearch == other.instructionsSearch && metadata == other.metadata && model == other.model && reasoningEffort == other.reasoningEffort && temperature == other.temperature && topP == other.topP && users == other.users && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is InnerResponses && type == other.type && createdAfter == other.createdAfter && createdBefore == other.createdBefore && instructionsSearch == other.instructionsSearch && metadata == other.metadata && model == other.model && reasoningEffort == other.reasoningEffort && temperature == other.temperature && tools == other.tools && topP == other.topP && users == other.users && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(type, allowParallelToolCalls, createdAfter, createdBefore, hasToolCalls, instructionsSearch, metadata, model, reasoningEffort, temperature, topP, users, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(type, createdAfter, createdBefore, instructionsSearch, metadata, model, reasoningEffort, temperature, tools, topP, users, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "Responses{type=$type, allowParallelToolCalls=$allowParallelToolCalls, createdAfter=$createdAfter, createdBefore=$createdBefore, hasToolCalls=$hasToolCalls, instructionsSearch=$instructionsSearch, metadata=$metadata, model=$model, reasoningEffort=$reasoningEffort, temperature=$temperature, topP=$topP, users=$users, additionalProperties=$additionalProperties}" + "InnerResponses{type=$type, createdAfter=$createdAfter, createdBefore=$createdBefore, instructionsSearch=$instructionsSearch, metadata=$metadata, model=$model, reasoningEffort=$reasoningEffort, temperature=$temperature, tools=$tools, topP=$topP, users=$users, additionalProperties=$additionalProperties}" } } + /** + * Used when sampling from a model. Dictates the structure of the messages passed into + * the model. Can either be a reference to a prebuilt trajectory (ie, + * `item.input_trajectory`), or a template with variable references to the `item` + * namespace. + */ @JsonDeserialize(using = InputMessages.Deserializer::class) @JsonSerialize(using = InputMessages.Serializer::class) class InputMessages @@ -3554,7 +3465,7 @@ private constructor( /** * A list of chat messages forming the prompt or context. May include variable - * references to the "item" namespace, ie {{item.name}}. + * references to the `item` namespace, ie {{item.name}}. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * or is unexpectedly missing or null (e.g. if the server responded with an @@ -3627,7 +3538,7 @@ private constructor( /** * A list of chat messages forming the prompt or context. May include - * variable references to the "item" namespace, ie {{item.name}}. + * variable references to the `item` namespace, ie {{item.name}}. */ fun template(template: List) = template(JsonField.of(template)) @@ -5304,7 +5215,7 @@ private constructor( ) : this(itemReference, type, mutableMapOf()) /** - * A reference to a variable in the "item" namespace. Ie, "item.name" + * A reference to a variable in the `item` namespace. Ie, "item.name" * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type * or is unexpectedly missing or null (e.g. if the server responded with an @@ -5376,7 +5287,7 @@ private constructor( additionalProperties = itemReference.additionalProperties.toMutableMap() } - /** A reference to a variable in the "item" namespace. Ie, "item.name" */ + /** A reference to a variable in the `item` namespace. Ie, "item.name" */ fun itemReference(itemReference: String) = itemReference(JsonField.of(itemReference)) @@ -5781,7 +5692,7 @@ private constructor( return true } - return /* spotless:off */ other is Completions && source == other.source && type == other.type && inputMessages == other.inputMessages && model == other.model && samplingParams == other.samplingParams && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is Responses && source == other.source && type == other.type && inputMessages == other.inputMessages && model == other.model && samplingParams == other.samplingParams && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ @@ -5791,7 +5702,7 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "Completions{source=$source, type=$type, inputMessages=$inputMessages, model=$model, samplingParams=$samplingParams, additionalProperties=$additionalProperties}" + "Responses{source=$source, type=$type, inputMessages=$inputMessages, model=$model, samplingParams=$samplingParams, additionalProperties=$additionalProperties}" } } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemListPage.kt index e24395a2..b75e0d39 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemListPage.kt @@ -2,12 +2,12 @@ package com.openai.models.evals.runs.outputitems +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.evals.runs.OutputItemService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [OutputItemService.list] */ @@ -16,7 +16,7 @@ private constructor( private val service: OutputItemService, private val params: OutputItemListParams, private val response: OutputItemListPageResponse, -) { +) : Page { /** * Delegates to [OutputItemListPageResponse], but gracefully handles missing data. @@ -33,19 +33,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): OutputItemListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + override fun nextPage(): OutputItemListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): OutputItemListParams = params @@ -114,25 +111,6 @@ private constructor( ) } - class AutoPager(private val firstPage: OutputItemListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemListPageAsync.kt index 9e5c9635..c12bf482 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemListPageAsync.kt @@ -2,22 +2,24 @@ package com.openai.models.evals.runs.outputitems +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.evals.runs.OutputItemServiceAsync import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [OutputItemServiceAsync.list] */ class OutputItemListPageAsync private constructor( private val service: OutputItemServiceAsync, + private val streamHandlerExecutor: Executor, private val params: OutputItemListParams, private val response: OutputItemListPageResponse, -) { +) : PageAsync { /** * Delegates to [OutputItemListPageResponse], but gracefully handles missing data. @@ -34,22 +36,18 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): OutputItemListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = + service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = + AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): OutputItemListParams = params @@ -67,6 +65,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -78,18 +77,24 @@ private constructor( class Builder internal constructor() { private var service: OutputItemServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: OutputItemListParams? = null private var response: OutputItemListPageResponse? = null @JvmSynthetic internal fun from(outputItemListPageAsync: OutputItemListPageAsync) = apply { service = outputItemListPageAsync.service + streamHandlerExecutor = outputItemListPageAsync.streamHandlerExecutor params = outputItemListPageAsync.params response = outputItemListPageAsync.response } fun service(service: OutputItemServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: OutputItemListParams) = apply { this.params = params } @@ -104,6 +109,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -113,50 +119,22 @@ private constructor( fun build(): OutputItemListPageAsync = OutputItemListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: OutputItemListPageAsync) { - - fun forEach( - action: Predicate, - executor: Executor, - ): CompletableFuture { - fun CompletableFuture>.forEach( - action: (OutputItemListResponse) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is OutputItemListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is OutputItemListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "OutputItemListPageAsync{service=$service, params=$params, response=$response}" + "OutputItemListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemListParams.kt index 4c6c896b..d7b88210 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemListParams.kt @@ -18,7 +18,7 @@ import kotlin.jvm.optionals.getOrNull class OutputItemListParams private constructor( private val evalId: String, - private val runId: String, + private val runId: String?, private val after: String?, private val limit: Long?, private val order: Order?, @@ -29,7 +29,7 @@ private constructor( fun evalId(): String = evalId - fun runId(): String = runId + fun runId(): Optional = Optional.ofNullable(runId) /** Identifier for the last output item from the previous pagination request. */ fun after(): Optional = Optional.ofNullable(after) @@ -63,7 +63,6 @@ private constructor( * The following fields are required: * ```java * .evalId() - * .runId() * ``` */ @JvmStatic fun builder() = Builder() @@ -95,7 +94,10 @@ private constructor( fun evalId(evalId: String) = apply { this.evalId = evalId } - fun runId(runId: String) = apply { this.runId = runId } + fun runId(runId: String?) = apply { this.runId = runId } + + /** Alias for calling [Builder.runId] with `runId.orElse(null)`. */ + fun runId(runId: Optional) = runId(runId.getOrNull()) /** Identifier for the last output item from the previous pagination request. */ fun after(after: String?) = apply { this.after = after } @@ -240,7 +242,6 @@ private constructor( * The following fields are required: * ```java * .evalId() - * .runId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -248,7 +249,7 @@ private constructor( fun build(): OutputItemListParams = OutputItemListParams( checkRequired("evalId", evalId), - checkRequired("runId", runId), + runId, after, limit, order, @@ -261,7 +262,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> evalId - 1 -> runId + 1 -> runId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemRetrieveParams.kt index 3d1a3ce3..361599f5 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemRetrieveParams.kt @@ -7,13 +7,15 @@ import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Get an evaluation run output item by ID. */ class OutputItemRetrieveParams private constructor( private val evalId: String, private val runId: String, - private val outputItemId: String, + private val outputItemId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { @@ -22,7 +24,7 @@ private constructor( fun runId(): String = runId - fun outputItemId(): String = outputItemId + fun outputItemId(): Optional = Optional.ofNullable(outputItemId) fun _additionalHeaders(): Headers = additionalHeaders @@ -39,7 +41,6 @@ private constructor( * ```java * .evalId() * .runId() - * .outputItemId() * ``` */ @JvmStatic fun builder() = Builder() @@ -67,7 +68,10 @@ private constructor( fun runId(runId: String) = apply { this.runId = runId } - fun outputItemId(outputItemId: String) = apply { this.outputItemId = outputItemId } + fun outputItemId(outputItemId: String?) = apply { this.outputItemId = outputItemId } + + /** Alias for calling [Builder.outputItemId] with `outputItemId.orElse(null)`. */ + fun outputItemId(outputItemId: Optional) = outputItemId(outputItemId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -176,7 +180,6 @@ private constructor( * ```java * .evalId() * .runId() - * .outputItemId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -185,7 +188,7 @@ private constructor( OutputItemRetrieveParams( checkRequired("evalId", evalId), checkRequired("runId", runId), - checkRequired("outputItemId", outputItemId), + outputItemId, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -195,7 +198,7 @@ private constructor( when (index) { 0 -> evalId 1 -> runId - 2 -> outputItemId + 2 -> outputItemId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/files/FileContentParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/files/FileContentParams.kt index 0a4ea8ab..5035b168 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/files/FileContentParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/files/FileContentParams.kt @@ -3,20 +3,21 @@ package com.openai.models.files import com.openai.core.Params -import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Returns the contents of the specified file. */ class FileContentParams private constructor( - private val fileId: String, + private val fileId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun fileId(): String = fileId + fun fileId(): Optional = Optional.ofNullable(fileId) fun _additionalHeaders(): Headers = additionalHeaders @@ -26,14 +27,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [FileContentParams]. - * - * The following fields are required: - * ```java - * .fileId() - * ``` - */ + @JvmStatic fun none(): FileContentParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [FileContentParams]. */ @JvmStatic fun builder() = Builder() } @@ -51,7 +47,10 @@ private constructor( additionalQueryParams = fileContentParams.additionalQueryParams.toBuilder() } - fun fileId(fileId: String) = apply { this.fileId = fileId } + fun fileId(fileId: String?) = apply { this.fileId = fileId } + + /** Alias for calling [Builder.fileId] with `fileId.orElse(null)`. */ + fun fileId(fileId: Optional) = fileId(fileId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -155,25 +154,14 @@ private constructor( * Returns an immutable instance of [FileContentParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .fileId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): FileContentParams = - FileContentParams( - checkRequired("fileId", fileId), - additionalHeaders.build(), - additionalQueryParams.build(), - ) + FileContentParams(fileId, additionalHeaders.build(), additionalQueryParams.build()) } fun _pathParam(index: Int): String = when (index) { - 0 -> fileId + 0 -> fileId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/files/FileDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/files/FileDeleteParams.kt index 2bc93b6a..dca35f8a 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/files/FileDeleteParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/files/FileDeleteParams.kt @@ -4,23 +4,23 @@ package com.openai.models.files import com.openai.core.JsonValue 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 +import kotlin.jvm.optionals.getOrNull /** Delete a file. */ class FileDeleteParams private constructor( - private val fileId: String, + private val fileId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, ) : Params { - fun fileId(): String = fileId + fun fileId(): Optional = Optional.ofNullable(fileId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -32,14 +32,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [FileDeleteParams]. - * - * The following fields are required: - * ```java - * .fileId() - * ``` - */ + @JvmStatic fun none(): FileDeleteParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [FileDeleteParams]. */ @JvmStatic fun builder() = Builder() } @@ -59,7 +54,10 @@ private constructor( additionalBodyProperties = fileDeleteParams.additionalBodyProperties.toMutableMap() } - fun fileId(fileId: String) = apply { this.fileId = fileId } + fun fileId(fileId: String?) = apply { this.fileId = fileId } + + /** Alias for calling [Builder.fileId] with `fileId.orElse(null)`. */ + fun fileId(fileId: Optional) = fileId(fileId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -185,17 +183,10 @@ private constructor( * Returns an immutable instance of [FileDeleteParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .fileId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): FileDeleteParams = FileDeleteParams( - checkRequired("fileId", fileId), + fileId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -207,7 +198,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> fileId + 0 -> fileId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPage.kt index 2aa16da3..9310e134 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPage.kt @@ -2,12 +2,12 @@ package com.openai.models.files +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.FileService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [FileService.list] */ @@ -16,7 +16,7 @@ private constructor( private val service: FileService, private val params: FileListParams, private val response: FileListPageResponse, -) { +) : Page { /** * Delegates to [FileListPageResponse], but gracefully handles missing data. @@ -32,19 +32,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): FileListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + override fun nextPage(): FileListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): FileListParams = params @@ -113,25 +110,6 @@ private constructor( ) } - class AutoPager(private val firstPage: FileListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPageAsync.kt index ec8e2d6c..7f775f0c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPageAsync.kt @@ -2,22 +2,24 @@ package com.openai.models.files +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.FileServiceAsync import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [FileServiceAsync.list] */ class FileListPageAsync private constructor( private val service: FileServiceAsync, + private val streamHandlerExecutor: Executor, private val params: FileListParams, private val response: FileListPageResponse, -) { +) : PageAsync { /** * Delegates to [FileListPageResponse], but gracefully handles missing data. @@ -33,22 +35,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): FileListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): FileListParams = params @@ -66,6 +62,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -77,18 +74,24 @@ private constructor( class Builder internal constructor() { private var service: FileServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: FileListParams? = null private var response: FileListPageResponse? = null @JvmSynthetic internal fun from(fileListPageAsync: FileListPageAsync) = apply { service = fileListPageAsync.service + streamHandlerExecutor = fileListPageAsync.streamHandlerExecutor params = fileListPageAsync.params response = fileListPageAsync.response } fun service(service: FileServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: FileListParams) = apply { this.params = params } @@ -103,6 +106,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -112,47 +116,22 @@ private constructor( fun build(): FileListPageAsync = FileListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: FileListPageAsync) { - - fun forEach(action: Predicate, executor: Executor): CompletableFuture { - fun CompletableFuture>.forEach( - action: (FileObject) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is FileListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is FileListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "FileListPageAsync{service=$service, params=$params, response=$response}" + "FileListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/files/FileRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/files/FileRetrieveParams.kt index 379eb2f1..cb00e708 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/files/FileRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/files/FileRetrieveParams.kt @@ -3,20 +3,21 @@ package com.openai.models.files import com.openai.core.Params -import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Returns information about a specific file. */ class FileRetrieveParams private constructor( - private val fileId: String, + private val fileId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun fileId(): String = fileId + fun fileId(): Optional = Optional.ofNullable(fileId) fun _additionalHeaders(): Headers = additionalHeaders @@ -26,14 +27,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [FileRetrieveParams]. - * - * The following fields are required: - * ```java - * .fileId() - * ``` - */ + @JvmStatic fun none(): FileRetrieveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [FileRetrieveParams]. */ @JvmStatic fun builder() = Builder() } @@ -51,7 +47,10 @@ private constructor( additionalQueryParams = fileRetrieveParams.additionalQueryParams.toBuilder() } - fun fileId(fileId: String) = apply { this.fileId = fileId } + fun fileId(fileId: String?) = apply { this.fileId = fileId } + + /** Alias for calling [Builder.fileId] with `fileId.orElse(null)`. */ + fun fileId(fileId: Optional) = fileId(fileId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -155,25 +154,14 @@ private constructor( * Returns an immutable instance of [FileRetrieveParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .fileId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): FileRetrieveParams = - FileRetrieveParams( - checkRequired("fileId", fileId), - additionalHeaders.build(), - additionalQueryParams.build(), - ) + FileRetrieveParams(fileId, additionalHeaders.build(), additionalQueryParams.build()) } fun _pathParam(index: Int): String = when (index) { - 0 -> fileId + 0 -> fileId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunParams.kt new file mode 100644 index 00000000..cd49645d --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunParams.kt @@ -0,0 +1,1145 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.alpha.graders + +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.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.ExcludeMissing +import com.openai.core.JsonField +import com.openai.core.JsonMissing +import com.openai.core.JsonValue +import com.openai.core.Params +import com.openai.core.allMaxBy +import com.openai.core.checkRequired +import com.openai.core.getOrThrow +import com.openai.core.http.Headers +import com.openai.core.http.QueryParams +import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.graders.gradermodels.MultiGrader +import com.openai.models.graders.gradermodels.PythonGrader +import com.openai.models.graders.gradermodels.ScoreModelGrader +import com.openai.models.graders.gradermodels.StringCheckGrader +import com.openai.models.graders.gradermodels.TextSimilarityGrader +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** Run a grader. */ +class GraderRunParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * The grader used for the fine-tuning job. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun grader(): Grader = body.grader() + + /** + * The model sample to be evaluated. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun modelSample(): String = body.modelSample() + + /** + * The reference answer for the evaluation. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun referenceAnswer(): ReferenceAnswer = body.referenceAnswer() + + /** + * Returns the raw JSON value of [grader]. + * + * Unlike [grader], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _grader(): JsonField = body._grader() + + /** + * Returns the raw JSON value of [modelSample]. + * + * Unlike [modelSample], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _modelSample(): JsonField = body._modelSample() + + /** + * Returns the raw JSON value of [referenceAnswer]. + * + * Unlike [referenceAnswer], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _referenceAnswer(): JsonField = body._referenceAnswer() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [GraderRunParams]. + * + * The following fields are required: + * ```java + * .grader() + * .modelSample() + * .referenceAnswer() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [GraderRunParams]. */ + 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(graderRunParams: GraderRunParams) = apply { + body = graderRunParams.body.toBuilder() + additionalHeaders = graderRunParams.additionalHeaders.toBuilder() + additionalQueryParams = graderRunParams.additionalQueryParams.toBuilder() + } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [grader] + * - [modelSample] + * - [referenceAnswer] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** The grader used for the fine-tuning job. */ + fun grader(grader: Grader) = apply { body.grader(grader) } + + /** + * Sets [Builder.grader] to an arbitrary JSON value. + * + * You should usually call [Builder.grader] with a well-typed [Grader] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun grader(grader: JsonField) = apply { body.grader(grader) } + + /** Alias for calling [grader] with `Grader.ofStringCheck(stringCheck)`. */ + fun grader(stringCheck: StringCheckGrader) = apply { body.grader(stringCheck) } + + /** Alias for calling [grader] with `Grader.ofTextSimilarity(textSimilarity)`. */ + fun grader(textSimilarity: TextSimilarityGrader) = apply { body.grader(textSimilarity) } + + /** Alias for calling [grader] with `Grader.ofPython(python)`. */ + fun grader(python: PythonGrader) = apply { body.grader(python) } + + /** Alias for calling [grader] with `Grader.ofScoreModel(scoreModel)`. */ + fun grader(scoreModel: ScoreModelGrader) = apply { body.grader(scoreModel) } + + /** Alias for calling [grader] with `Grader.ofMulti(multi)`. */ + fun grader(multi: MultiGrader) = apply { body.grader(multi) } + + /** The model sample to be evaluated. */ + fun modelSample(modelSample: String) = apply { body.modelSample(modelSample) } + + /** + * Sets [Builder.modelSample] to an arbitrary JSON value. + * + * You should usually call [Builder.modelSample] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun modelSample(modelSample: JsonField) = apply { body.modelSample(modelSample) } + + /** The reference answer for the evaluation. */ + fun referenceAnswer(referenceAnswer: ReferenceAnswer) = apply { + body.referenceAnswer(referenceAnswer) + } + + /** + * Sets [Builder.referenceAnswer] to an arbitrary JSON value. + * + * You should usually call [Builder.referenceAnswer] with a well-typed [ReferenceAnswer] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun referenceAnswer(referenceAnswer: JsonField) = apply { + body.referenceAnswer(referenceAnswer) + } + + /** Alias for calling [referenceAnswer] with `ReferenceAnswer.ofString(string)`. */ + fun referenceAnswer(string: String) = apply { body.referenceAnswer(string) } + + /** Alias for calling [referenceAnswer] with `ReferenceAnswer.ofJsonValue(jsonValue)`. */ + fun referenceAnswer(jsonValue: JsonValue) = apply { body.referenceAnswer(jsonValue) } + + /** Alias for calling [referenceAnswer] with `ReferenceAnswer.ofJsonValues(jsonValues)`. */ + fun referenceAnswerOfJsonValues(jsonValues: List) = apply { + body.referenceAnswerOfJsonValues(jsonValues) + } + + /** Alias for calling [referenceAnswer] with `ReferenceAnswer.ofNumber(number)`. */ + fun referenceAnswer(number: Double) = apply { body.referenceAnswer(number) } + + 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) + } + + /** + * Returns an immutable instance of [GraderRunParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .grader() + * .modelSample() + * .referenceAnswer() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): GraderRunParams = + GraderRunParams(body.build(), additionalHeaders.build(), additionalQueryParams.build()) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + private constructor( + private val grader: JsonField, + private val modelSample: JsonField, + private val referenceAnswer: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("grader") @ExcludeMissing grader: JsonField = JsonMissing.of(), + @JsonProperty("model_sample") + @ExcludeMissing + modelSample: JsonField = JsonMissing.of(), + @JsonProperty("reference_answer") + @ExcludeMissing + referenceAnswer: JsonField = JsonMissing.of(), + ) : this(grader, modelSample, referenceAnswer, mutableMapOf()) + + /** + * The grader used for the fine-tuning job. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun grader(): Grader = grader.getRequired("grader") + + /** + * The model sample to be evaluated. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun modelSample(): String = modelSample.getRequired("model_sample") + + /** + * The reference answer for the evaluation. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun referenceAnswer(): ReferenceAnswer = referenceAnswer.getRequired("reference_answer") + + /** + * Returns the raw JSON value of [grader]. + * + * Unlike [grader], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("grader") @ExcludeMissing fun _grader(): JsonField = grader + + /** + * Returns the raw JSON value of [modelSample]. + * + * Unlike [modelSample], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("model_sample") + @ExcludeMissing + fun _modelSample(): JsonField = modelSample + + /** + * Returns the raw JSON value of [referenceAnswer]. + * + * Unlike [referenceAnswer], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("reference_answer") + @ExcludeMissing + fun _referenceAnswer(): JsonField = referenceAnswer + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```java + * .grader() + * .modelSample() + * .referenceAnswer() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var grader: JsonField? = null + private var modelSample: JsonField? = null + private var referenceAnswer: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + grader = body.grader + modelSample = body.modelSample + referenceAnswer = body.referenceAnswer + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** The grader used for the fine-tuning job. */ + fun grader(grader: Grader) = grader(JsonField.of(grader)) + + /** + * Sets [Builder.grader] to an arbitrary JSON value. + * + * You should usually call [Builder.grader] with a well-typed [Grader] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun grader(grader: JsonField) = apply { this.grader = grader } + + /** Alias for calling [grader] with `Grader.ofStringCheck(stringCheck)`. */ + fun grader(stringCheck: StringCheckGrader) = grader(Grader.ofStringCheck(stringCheck)) + + /** Alias for calling [grader] with `Grader.ofTextSimilarity(textSimilarity)`. */ + fun grader(textSimilarity: TextSimilarityGrader) = + grader(Grader.ofTextSimilarity(textSimilarity)) + + /** Alias for calling [grader] with `Grader.ofPython(python)`. */ + fun grader(python: PythonGrader) = grader(Grader.ofPython(python)) + + /** Alias for calling [grader] with `Grader.ofScoreModel(scoreModel)`. */ + fun grader(scoreModel: ScoreModelGrader) = grader(Grader.ofScoreModel(scoreModel)) + + /** Alias for calling [grader] with `Grader.ofMulti(multi)`. */ + fun grader(multi: MultiGrader) = grader(Grader.ofMulti(multi)) + + /** The model sample to be evaluated. */ + fun modelSample(modelSample: String) = modelSample(JsonField.of(modelSample)) + + /** + * Sets [Builder.modelSample] to an arbitrary JSON value. + * + * You should usually call [Builder.modelSample] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun modelSample(modelSample: JsonField) = apply { + this.modelSample = modelSample + } + + /** The reference answer for the evaluation. */ + fun referenceAnswer(referenceAnswer: ReferenceAnswer) = + referenceAnswer(JsonField.of(referenceAnswer)) + + /** + * Sets [Builder.referenceAnswer] to an arbitrary JSON value. + * + * You should usually call [Builder.referenceAnswer] with a well-typed [ReferenceAnswer] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun referenceAnswer(referenceAnswer: JsonField) = apply { + this.referenceAnswer = referenceAnswer + } + + /** Alias for calling [referenceAnswer] with `ReferenceAnswer.ofString(string)`. */ + fun referenceAnswer(string: String) = referenceAnswer(ReferenceAnswer.ofString(string)) + + /** + * Alias for calling [referenceAnswer] with `ReferenceAnswer.ofJsonValue(jsonValue)`. + */ + fun referenceAnswer(jsonValue: JsonValue) = + referenceAnswer(ReferenceAnswer.ofJsonValue(jsonValue)) + + /** + * Alias for calling [referenceAnswer] with `ReferenceAnswer.ofJsonValues(jsonValues)`. + */ + fun referenceAnswerOfJsonValues(jsonValues: List) = + referenceAnswer(ReferenceAnswer.ofJsonValues(jsonValues)) + + /** Alias for calling [referenceAnswer] with `ReferenceAnswer.ofNumber(number)`. */ + fun referenceAnswer(number: Double) = referenceAnswer(ReferenceAnswer.ofNumber(number)) + + 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) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .grader() + * .modelSample() + * .referenceAnswer() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("grader", grader), + checkRequired("modelSample", modelSample), + checkRequired("referenceAnswer", referenceAnswer), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + grader().validate() + modelSample() + referenceAnswer().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (grader.asKnown().getOrNull()?.validity() ?: 0) + + (if (modelSample.asKnown().isPresent) 1 else 0) + + (referenceAnswer.asKnown().getOrNull()?.validity() ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && grader == other.grader && modelSample == other.modelSample && referenceAnswer == other.referenceAnswer && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(grader, modelSample, referenceAnswer, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{grader=$grader, modelSample=$modelSample, referenceAnswer=$referenceAnswer, additionalProperties=$additionalProperties}" + } + + /** The grader used for the fine-tuning job. */ + @JsonDeserialize(using = Grader.Deserializer::class) + @JsonSerialize(using = Grader.Serializer::class) + class Grader + private constructor( + private val stringCheck: StringCheckGrader? = null, + private val textSimilarity: TextSimilarityGrader? = null, + private val python: PythonGrader? = null, + private val scoreModel: ScoreModelGrader? = null, + private val multi: MultiGrader? = null, + private val _json: JsonValue? = null, + ) { + + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun stringCheck(): Optional = Optional.ofNullable(stringCheck) + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun textSimilarity(): Optional = Optional.ofNullable(textSimilarity) + + /** A PythonGrader object that runs a python script on the input. */ + fun python(): Optional = Optional.ofNullable(python) + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun scoreModel(): Optional = Optional.ofNullable(scoreModel) + + /** + * A MultiGrader object combines the output of multiple graders to produce a single score. + */ + fun multi(): Optional = Optional.ofNullable(multi) + + fun isStringCheck(): Boolean = stringCheck != null + + fun isTextSimilarity(): Boolean = textSimilarity != null + + fun isPython(): Boolean = python != null + + fun isScoreModel(): Boolean = scoreModel != null + + fun isMulti(): Boolean = multi != null + + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun asStringCheck(): StringCheckGrader = stringCheck.getOrThrow("stringCheck") + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun asTextSimilarity(): TextSimilarityGrader = textSimilarity.getOrThrow("textSimilarity") + + /** A PythonGrader object that runs a python script on the input. */ + fun asPython(): PythonGrader = python.getOrThrow("python") + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun asScoreModel(): ScoreModelGrader = scoreModel.getOrThrow("scoreModel") + + /** + * A MultiGrader object combines the output of multiple graders to produce a single score. + */ + fun asMulti(): MultiGrader = multi.getOrThrow("multi") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + stringCheck != null -> visitor.visitStringCheck(stringCheck) + textSimilarity != null -> visitor.visitTextSimilarity(textSimilarity) + python != null -> visitor.visitPython(python) + scoreModel != null -> visitor.visitScoreModel(scoreModel) + multi != null -> visitor.visitMulti(multi) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Grader = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitStringCheck(stringCheck: StringCheckGrader) { + stringCheck.validate() + } + + override fun visitTextSimilarity(textSimilarity: TextSimilarityGrader) { + textSimilarity.validate() + } + + override fun visitPython(python: PythonGrader) { + python.validate() + } + + override fun visitScoreModel(scoreModel: ScoreModelGrader) { + scoreModel.validate() + } + + override fun visitMulti(multi: MultiGrader) { + multi.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitStringCheck(stringCheck: StringCheckGrader) = + stringCheck.validity() + + override fun visitTextSimilarity(textSimilarity: TextSimilarityGrader) = + textSimilarity.validity() + + override fun visitPython(python: PythonGrader) = python.validity() + + override fun visitScoreModel(scoreModel: ScoreModelGrader) = + scoreModel.validity() + + override fun visitMulti(multi: MultiGrader) = multi.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Grader && stringCheck == other.stringCheck && textSimilarity == other.textSimilarity && python == other.python && scoreModel == other.scoreModel && multi == other.multi /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(stringCheck, textSimilarity, python, scoreModel, multi) /* spotless:on */ + + override fun toString(): String = + when { + stringCheck != null -> "Grader{stringCheck=$stringCheck}" + textSimilarity != null -> "Grader{textSimilarity=$textSimilarity}" + python != null -> "Grader{python=$python}" + scoreModel != null -> "Grader{scoreModel=$scoreModel}" + multi != null -> "Grader{multi=$multi}" + _json != null -> "Grader{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Grader") + } + + companion object { + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + @JvmStatic + fun ofStringCheck(stringCheck: StringCheckGrader) = Grader(stringCheck = stringCheck) + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + @JvmStatic + fun ofTextSimilarity(textSimilarity: TextSimilarityGrader) = + Grader(textSimilarity = textSimilarity) + + /** A PythonGrader object that runs a python script on the input. */ + @JvmStatic fun ofPython(python: PythonGrader) = Grader(python = python) + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + @JvmStatic + fun ofScoreModel(scoreModel: ScoreModelGrader) = Grader(scoreModel = scoreModel) + + /** + * A MultiGrader object combines the output of multiple graders to produce a single + * score. + */ + @JvmStatic fun ofMulti(multi: MultiGrader) = Grader(multi = multi) + } + + /** An interface that defines how to map each variant of [Grader] to a value of type [T]. */ + interface Visitor { + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + fun visitStringCheck(stringCheck: StringCheckGrader): T + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun visitTextSimilarity(textSimilarity: TextSimilarityGrader): T + + /** A PythonGrader object that runs a python script on the input. */ + fun visitPython(python: PythonGrader): T + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun visitScoreModel(scoreModel: ScoreModelGrader): T + + /** + * A MultiGrader object combines the output of multiple graders to produce a single + * score. + */ + fun visitMulti(multi: MultiGrader): T + + /** + * Maps an unknown variant of [Grader] to a value of type [T]. + * + * An instance of [Grader] 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 Grader: $json") + } + } + + internal class Deserializer : BaseDeserializer(Grader::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Grader { + val json = JsonValue.fromJsonNode(node) + val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() + + when (type) { + "string_check" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + Grader(stringCheck = it, _json = json) + } ?: Grader(_json = json) + } + "text_similarity" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + Grader(textSimilarity = it, _json = json) + } ?: Grader(_json = json) + } + "python" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + Grader(python = it, _json = json) + } ?: Grader(_json = json) + } + "score_model" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + Grader(scoreModel = it, _json = json) + } ?: Grader(_json = json) + } + "multi" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + Grader(multi = it, _json = json) + } ?: Grader(_json = json) + } + } + + return Grader(_json = json) + } + } + + internal class Serializer : BaseSerializer(Grader::class) { + + override fun serialize( + value: Grader, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.stringCheck != null -> generator.writeObject(value.stringCheck) + value.textSimilarity != null -> generator.writeObject(value.textSimilarity) + value.python != null -> generator.writeObject(value.python) + value.scoreModel != null -> generator.writeObject(value.scoreModel) + value.multi != null -> generator.writeObject(value.multi) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Grader") + } + } + } + } + + /** The reference answer for the evaluation. */ + @JsonDeserialize(using = ReferenceAnswer.Deserializer::class) + @JsonSerialize(using = ReferenceAnswer.Serializer::class) + class ReferenceAnswer + private constructor( + private val string: String? = null, + private val jsonValue: JsonValue? = null, + private val jsonValues: List? = null, + private val number: Double? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun jsonValue(): Optional = Optional.ofNullable(jsonValue) + + fun jsonValues(): Optional> = Optional.ofNullable(jsonValues) + + fun number(): Optional = Optional.ofNullable(number) + + fun isString(): Boolean = string != null + + fun isJsonValue(): Boolean = jsonValue != null + + fun isJsonValues(): Boolean = jsonValues != null + + fun isNumber(): Boolean = number != null + + fun asString(): String = string.getOrThrow("string") + + fun asJsonValue(): JsonValue = jsonValue.getOrThrow("jsonValue") + + fun asJsonValues(): List = jsonValues.getOrThrow("jsonValues") + + fun asNumber(): Double = number.getOrThrow("number") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + jsonValue != null -> visitor.visitJsonValue(jsonValue) + jsonValues != null -> visitor.visitJsonValues(jsonValues) + number != null -> visitor.visitNumber(number) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): ReferenceAnswer = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitJsonValue(jsonValue: JsonValue) {} + + override fun visitJsonValues(jsonValues: List) {} + + override fun visitNumber(number: Double) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitJsonValue(jsonValue: JsonValue) = 1 + + override fun visitJsonValues(jsonValues: List) = jsonValues.size + + override fun visitNumber(number: Double) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is ReferenceAnswer && string == other.string && jsonValue == other.jsonValue && jsonValues == other.jsonValues && number == other.number /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(string, jsonValue, jsonValues, number) /* spotless:on */ + + override fun toString(): String = + when { + string != null -> "ReferenceAnswer{string=$string}" + jsonValue != null -> "ReferenceAnswer{jsonValue=$jsonValue}" + jsonValues != null -> "ReferenceAnswer{jsonValues=$jsonValues}" + number != null -> "ReferenceAnswer{number=$number}" + _json != null -> "ReferenceAnswer{_unknown=$_json}" + else -> throw IllegalStateException("Invalid ReferenceAnswer") + } + + companion object { + + @JvmStatic fun ofString(string: String) = ReferenceAnswer(string = string) + + @JvmStatic + fun ofJsonValue(jsonValue: JsonValue) = ReferenceAnswer(jsonValue = jsonValue) + + @JvmStatic + fun ofJsonValues(jsonValues: List) = ReferenceAnswer(jsonValues = jsonValues) + + @JvmStatic fun ofNumber(number: Double) = ReferenceAnswer(number = number) + } + + /** + * An interface that defines how to map each variant of [ReferenceAnswer] to a value of type + * [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitJsonValue(jsonValue: JsonValue): T + + fun visitJsonValues(jsonValues: List): T + + fun visitNumber(number: Double): T + + /** + * Maps an unknown variant of [ReferenceAnswer] to a value of type [T]. + * + * An instance of [ReferenceAnswer] 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 ReferenceAnswer: $json") + } + } + + internal class Deserializer : BaseDeserializer(ReferenceAnswer::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): ReferenceAnswer { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + ReferenceAnswer(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>())?.let { + ReferenceAnswer(jsonValues = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ReferenceAnswer(number = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ReferenceAnswer(jsonValue = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants. + 0 -> ReferenceAnswer(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(ReferenceAnswer::class) { + + override fun serialize( + value: ReferenceAnswer, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.jsonValue != null -> generator.writeObject(value.jsonValue) + value.jsonValues != null -> generator.writeObject(value.jsonValues) + value.number != null -> generator.writeObject(value.number) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid ReferenceAnswer") + } + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is GraderRunParams && 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() = + "GraderRunParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunResponse.kt new file mode 100644 index 00000000..6523c6c5 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunResponse.kt @@ -0,0 +1,1772 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.alpha.graders + +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.checkRequired +import com.openai.core.toImmutable +import com.openai.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class GraderRunResponse +private constructor( + private val metadata: JsonField, + private val modelGraderTokenUsagePerModel: JsonField, + private val reward: JsonField, + private val subRewards: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("metadata") @ExcludeMissing metadata: JsonField = JsonMissing.of(), + @JsonProperty("model_grader_token_usage_per_model") + @ExcludeMissing + modelGraderTokenUsagePerModel: JsonField = JsonMissing.of(), + @JsonProperty("reward") @ExcludeMissing reward: JsonField = JsonMissing.of(), + @JsonProperty("sub_rewards") + @ExcludeMissing + subRewards: JsonField = JsonMissing.of(), + ) : this(metadata, modelGraderTokenUsagePerModel, reward, subRewards, mutableMapOf()) + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun metadata(): Metadata = metadata.getRequired("metadata") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun modelGraderTokenUsagePerModel(): ModelGraderTokenUsagePerModel = + modelGraderTokenUsagePerModel.getRequired("model_grader_token_usage_per_model") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun reward(): Double = reward.getRequired("reward") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun subRewards(): SubRewards = subRewards.getRequired("sub_rewards") + + /** + * Returns the raw JSON value of [metadata]. + * + * Unlike [metadata], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("metadata") @ExcludeMissing fun _metadata(): JsonField = metadata + + /** + * Returns the raw JSON value of [modelGraderTokenUsagePerModel]. + * + * Unlike [modelGraderTokenUsagePerModel], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("model_grader_token_usage_per_model") + @ExcludeMissing + fun _modelGraderTokenUsagePerModel(): JsonField = + modelGraderTokenUsagePerModel + + /** + * Returns the raw JSON value of [reward]. + * + * Unlike [reward], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("reward") @ExcludeMissing fun _reward(): JsonField = reward + + /** + * Returns the raw JSON value of [subRewards]. + * + * Unlike [subRewards], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sub_rewards") + @ExcludeMissing + fun _subRewards(): JsonField = subRewards + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [GraderRunResponse]. + * + * The following fields are required: + * ```java + * .metadata() + * .modelGraderTokenUsagePerModel() + * .reward() + * .subRewards() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [GraderRunResponse]. */ + class Builder internal constructor() { + + private var metadata: JsonField? = null + private var modelGraderTokenUsagePerModel: JsonField? = null + private var reward: JsonField? = null + private var subRewards: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(graderRunResponse: GraderRunResponse) = apply { + metadata = graderRunResponse.metadata + modelGraderTokenUsagePerModel = graderRunResponse.modelGraderTokenUsagePerModel + reward = graderRunResponse.reward + subRewards = graderRunResponse.subRewards + additionalProperties = graderRunResponse.additionalProperties.toMutableMap() + } + + fun metadata(metadata: Metadata) = metadata(JsonField.of(metadata)) + + /** + * Sets [Builder.metadata] to an arbitrary JSON value. + * + * You should usually call [Builder.metadata] with a well-typed [Metadata] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun metadata(metadata: JsonField) = apply { this.metadata = metadata } + + fun modelGraderTokenUsagePerModel( + modelGraderTokenUsagePerModel: ModelGraderTokenUsagePerModel + ) = modelGraderTokenUsagePerModel(JsonField.of(modelGraderTokenUsagePerModel)) + + /** + * Sets [Builder.modelGraderTokenUsagePerModel] to an arbitrary JSON value. + * + * You should usually call [Builder.modelGraderTokenUsagePerModel] with a well-typed + * [ModelGraderTokenUsagePerModel] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun modelGraderTokenUsagePerModel( + modelGraderTokenUsagePerModel: JsonField + ) = apply { this.modelGraderTokenUsagePerModel = modelGraderTokenUsagePerModel } + + fun reward(reward: Double) = reward(JsonField.of(reward)) + + /** + * Sets [Builder.reward] to an arbitrary JSON value. + * + * You should usually call [Builder.reward] with a well-typed [Double] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun reward(reward: JsonField) = apply { this.reward = reward } + + fun subRewards(subRewards: SubRewards) = subRewards(JsonField.of(subRewards)) + + /** + * Sets [Builder.subRewards] to an arbitrary JSON value. + * + * You should usually call [Builder.subRewards] with a well-typed [SubRewards] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun subRewards(subRewards: JsonField) = apply { this.subRewards = subRewards } + + 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) + } + + /** + * Returns an immutable instance of [GraderRunResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .metadata() + * .modelGraderTokenUsagePerModel() + * .reward() + * .subRewards() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): GraderRunResponse = + GraderRunResponse( + checkRequired("metadata", metadata), + checkRequired("modelGraderTokenUsagePerModel", modelGraderTokenUsagePerModel), + checkRequired("reward", reward), + checkRequired("subRewards", subRewards), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): GraderRunResponse = apply { + if (validated) { + return@apply + } + + metadata().validate() + modelGraderTokenUsagePerModel().validate() + reward() + subRewards().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (modelGraderTokenUsagePerModel.asKnown().getOrNull()?.validity() ?: 0) + + (if (reward.asKnown().isPresent) 1 else 0) + + (subRewards.asKnown().getOrNull()?.validity() ?: 0) + + class Metadata + private constructor( + private val errors: JsonField, + private val executionTime: JsonField, + private val name: JsonField, + private val sampledModelName: JsonField, + private val scores: JsonField, + private val tokenUsage: JsonField, + private val type: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("errors") @ExcludeMissing errors: JsonField = JsonMissing.of(), + @JsonProperty("execution_time") + @ExcludeMissing + executionTime: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("sampled_model_name") + @ExcludeMissing + sampledModelName: JsonField = JsonMissing.of(), + @JsonProperty("scores") @ExcludeMissing scores: JsonField = JsonMissing.of(), + @JsonProperty("token_usage") + @ExcludeMissing + tokenUsage: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + ) : this( + errors, + executionTime, + name, + sampledModelName, + scores, + tokenUsage, + type, + mutableMapOf(), + ) + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun errors(): Errors = errors.getRequired("errors") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun executionTime(): Double = executionTime.getRequired("execution_time") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun name(): String = name.getRequired("name") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun sampledModelName(): Optional = + sampledModelName.getOptional("sampled_model_name") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun scores(): Scores = scores.getRequired("scores") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun tokenUsage(): Optional = tokenUsage.getOptional("token_usage") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun type(): String = type.getRequired("type") + + /** + * Returns the raw JSON value of [errors]. + * + * Unlike [errors], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("errors") @ExcludeMissing fun _errors(): JsonField = errors + + /** + * Returns the raw JSON value of [executionTime]. + * + * Unlike [executionTime], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("execution_time") + @ExcludeMissing + fun _executionTime(): JsonField = executionTime + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + /** + * Returns the raw JSON value of [sampledModelName]. + * + * Unlike [sampledModelName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("sampled_model_name") + @ExcludeMissing + fun _sampledModelName(): JsonField = sampledModelName + + /** + * Returns the raw JSON value of [scores]. + * + * Unlike [scores], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("scores") @ExcludeMissing fun _scores(): JsonField = scores + + /** + * Returns the raw JSON value of [tokenUsage]. + * + * Unlike [tokenUsage], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("token_usage") @ExcludeMissing fun _tokenUsage(): JsonField = tokenUsage + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Metadata]. + * + * The following fields are required: + * ```java + * .errors() + * .executionTime() + * .name() + * .sampledModelName() + * .scores() + * .tokenUsage() + * .type() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Metadata]. */ + class Builder internal constructor() { + + private var errors: JsonField? = null + private var executionTime: JsonField? = null + private var name: JsonField? = null + private var sampledModelName: JsonField? = null + private var scores: JsonField? = null + private var tokenUsage: JsonField? = null + private var type: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(metadata: Metadata) = apply { + errors = metadata.errors + executionTime = metadata.executionTime + name = metadata.name + sampledModelName = metadata.sampledModelName + scores = metadata.scores + tokenUsage = metadata.tokenUsage + type = metadata.type + additionalProperties = metadata.additionalProperties.toMutableMap() + } + + fun errors(errors: Errors) = errors(JsonField.of(errors)) + + /** + * Sets [Builder.errors] to an arbitrary JSON value. + * + * You should usually call [Builder.errors] with a well-typed [Errors] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun errors(errors: JsonField) = apply { this.errors = errors } + + fun executionTime(executionTime: Double) = executionTime(JsonField.of(executionTime)) + + /** + * Sets [Builder.executionTime] to an arbitrary JSON value. + * + * You should usually call [Builder.executionTime] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun executionTime(executionTime: JsonField) = apply { + this.executionTime = executionTime + } + + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun name(name: JsonField) = apply { this.name = name } + + fun sampledModelName(sampledModelName: String?) = + sampledModelName(JsonField.ofNullable(sampledModelName)) + + /** + * Alias for calling [Builder.sampledModelName] with `sampledModelName.orElse(null)`. + */ + fun sampledModelName(sampledModelName: Optional) = + sampledModelName(sampledModelName.getOrNull()) + + /** + * Sets [Builder.sampledModelName] to an arbitrary JSON value. + * + * You should usually call [Builder.sampledModelName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sampledModelName(sampledModelName: JsonField) = apply { + this.sampledModelName = sampledModelName + } + + fun scores(scores: Scores) = scores(JsonField.of(scores)) + + /** + * Sets [Builder.scores] to an arbitrary JSON value. + * + * You should usually call [Builder.scores] with a well-typed [Scores] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun scores(scores: JsonField) = apply { this.scores = scores } + + fun tokenUsage(tokenUsage: Long?) = tokenUsage(JsonField.ofNullable(tokenUsage)) + + /** + * Alias for [Builder.tokenUsage]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun tokenUsage(tokenUsage: Long) = tokenUsage(tokenUsage as Long?) + + /** Alias for calling [Builder.tokenUsage] with `tokenUsage.orElse(null)`. */ + fun tokenUsage(tokenUsage: Optional) = tokenUsage(tokenUsage.getOrNull()) + + /** + * Sets [Builder.tokenUsage] to an arbitrary JSON value. + * + * You should usually call [Builder.tokenUsage] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun tokenUsage(tokenUsage: JsonField) = apply { this.tokenUsage = tokenUsage } + + fun type(type: String) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + 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) + } + + /** + * Returns an immutable instance of [Metadata]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .errors() + * .executionTime() + * .name() + * .sampledModelName() + * .scores() + * .tokenUsage() + * .type() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Metadata = + Metadata( + checkRequired("errors", errors), + checkRequired("executionTime", executionTime), + checkRequired("name", name), + checkRequired("sampledModelName", sampledModelName), + checkRequired("scores", scores), + checkRequired("tokenUsage", tokenUsage), + checkRequired("type", type), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Metadata = apply { + if (validated) { + return@apply + } + + errors().validate() + executionTime() + name() + sampledModelName() + scores().validate() + tokenUsage() + type() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (errors.asKnown().getOrNull()?.validity() ?: 0) + + (if (executionTime.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (sampledModelName.asKnown().isPresent) 1 else 0) + + (scores.asKnown().getOrNull()?.validity() ?: 0) + + (if (tokenUsage.asKnown().isPresent) 1 else 0) + + (if (type.asKnown().isPresent) 1 else 0) + + class Errors + private constructor( + private val formulaParseError: JsonField, + private val invalidVariableError: JsonField, + private val modelGraderParseError: JsonField, + private val modelGraderRefusalError: JsonField, + private val modelGraderServerError: JsonField, + private val modelGraderServerErrorDetails: JsonField, + private val otherError: JsonField, + private val pythonGraderRuntimeError: JsonField, + private val pythonGraderRuntimeErrorDetails: JsonField, + private val pythonGraderServerError: JsonField, + private val pythonGraderServerErrorType: JsonField, + private val sampleParseError: JsonField, + private val truncatedObservationError: JsonField, + private val unresponsiveRewardError: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("formula_parse_error") + @ExcludeMissing + formulaParseError: JsonField = JsonMissing.of(), + @JsonProperty("invalid_variable_error") + @ExcludeMissing + invalidVariableError: JsonField = JsonMissing.of(), + @JsonProperty("model_grader_parse_error") + @ExcludeMissing + modelGraderParseError: JsonField = JsonMissing.of(), + @JsonProperty("model_grader_refusal_error") + @ExcludeMissing + modelGraderRefusalError: JsonField = JsonMissing.of(), + @JsonProperty("model_grader_server_error") + @ExcludeMissing + modelGraderServerError: JsonField = JsonMissing.of(), + @JsonProperty("model_grader_server_error_details") + @ExcludeMissing + modelGraderServerErrorDetails: JsonField = JsonMissing.of(), + @JsonProperty("other_error") + @ExcludeMissing + otherError: JsonField = JsonMissing.of(), + @JsonProperty("python_grader_runtime_error") + @ExcludeMissing + pythonGraderRuntimeError: JsonField = JsonMissing.of(), + @JsonProperty("python_grader_runtime_error_details") + @ExcludeMissing + pythonGraderRuntimeErrorDetails: JsonField = JsonMissing.of(), + @JsonProperty("python_grader_server_error") + @ExcludeMissing + pythonGraderServerError: JsonField = JsonMissing.of(), + @JsonProperty("python_grader_server_error_type") + @ExcludeMissing + pythonGraderServerErrorType: JsonField = JsonMissing.of(), + @JsonProperty("sample_parse_error") + @ExcludeMissing + sampleParseError: JsonField = JsonMissing.of(), + @JsonProperty("truncated_observation_error") + @ExcludeMissing + truncatedObservationError: JsonField = JsonMissing.of(), + @JsonProperty("unresponsive_reward_error") + @ExcludeMissing + unresponsiveRewardError: JsonField = JsonMissing.of(), + ) : this( + formulaParseError, + invalidVariableError, + modelGraderParseError, + modelGraderRefusalError, + modelGraderServerError, + modelGraderServerErrorDetails, + otherError, + pythonGraderRuntimeError, + pythonGraderRuntimeErrorDetails, + pythonGraderServerError, + pythonGraderServerErrorType, + sampleParseError, + truncatedObservationError, + unresponsiveRewardError, + mutableMapOf(), + ) + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun formulaParseError(): Boolean = formulaParseError.getRequired("formula_parse_error") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun invalidVariableError(): Boolean = + invalidVariableError.getRequired("invalid_variable_error") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun modelGraderParseError(): Boolean = + modelGraderParseError.getRequired("model_grader_parse_error") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun modelGraderRefusalError(): Boolean = + modelGraderRefusalError.getRequired("model_grader_refusal_error") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun modelGraderServerError(): Boolean = + modelGraderServerError.getRequired("model_grader_server_error") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun modelGraderServerErrorDetails(): Optional = + modelGraderServerErrorDetails.getOptional("model_grader_server_error_details") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun otherError(): Boolean = otherError.getRequired("other_error") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun pythonGraderRuntimeError(): Boolean = + pythonGraderRuntimeError.getRequired("python_grader_runtime_error") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pythonGraderRuntimeErrorDetails(): Optional = + pythonGraderRuntimeErrorDetails.getOptional("python_grader_runtime_error_details") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun pythonGraderServerError(): Boolean = + pythonGraderServerError.getRequired("python_grader_server_error") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pythonGraderServerErrorType(): Optional = + pythonGraderServerErrorType.getOptional("python_grader_server_error_type") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun sampleParseError(): Boolean = sampleParseError.getRequired("sample_parse_error") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun truncatedObservationError(): Boolean = + truncatedObservationError.getRequired("truncated_observation_error") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun unresponsiveRewardError(): Boolean = + unresponsiveRewardError.getRequired("unresponsive_reward_error") + + /** + * Returns the raw JSON value of [formulaParseError]. + * + * Unlike [formulaParseError], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("formula_parse_error") + @ExcludeMissing + fun _formulaParseError(): JsonField = formulaParseError + + /** + * Returns the raw JSON value of [invalidVariableError]. + * + * Unlike [invalidVariableError], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("invalid_variable_error") + @ExcludeMissing + fun _invalidVariableError(): JsonField = invalidVariableError + + /** + * Returns the raw JSON value of [modelGraderParseError]. + * + * Unlike [modelGraderParseError], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("model_grader_parse_error") + @ExcludeMissing + fun _modelGraderParseError(): JsonField = modelGraderParseError + + /** + * Returns the raw JSON value of [modelGraderRefusalError]. + * + * Unlike [modelGraderRefusalError], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("model_grader_refusal_error") + @ExcludeMissing + fun _modelGraderRefusalError(): JsonField = modelGraderRefusalError + + /** + * Returns the raw JSON value of [modelGraderServerError]. + * + * Unlike [modelGraderServerError], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("model_grader_server_error") + @ExcludeMissing + fun _modelGraderServerError(): JsonField = modelGraderServerError + + /** + * Returns the raw JSON value of [modelGraderServerErrorDetails]. + * + * Unlike [modelGraderServerErrorDetails], this method doesn't throw if the JSON field + * has an unexpected type. + */ + @JsonProperty("model_grader_server_error_details") + @ExcludeMissing + fun _modelGraderServerErrorDetails(): JsonField = modelGraderServerErrorDetails + + /** + * Returns the raw JSON value of [otherError]. + * + * Unlike [otherError], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("other_error") + @ExcludeMissing + fun _otherError(): JsonField = otherError + + /** + * Returns the raw JSON value of [pythonGraderRuntimeError]. + * + * Unlike [pythonGraderRuntimeError], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("python_grader_runtime_error") + @ExcludeMissing + fun _pythonGraderRuntimeError(): JsonField = pythonGraderRuntimeError + + /** + * Returns the raw JSON value of [pythonGraderRuntimeErrorDetails]. + * + * Unlike [pythonGraderRuntimeErrorDetails], this method doesn't throw if the JSON field + * has an unexpected type. + */ + @JsonProperty("python_grader_runtime_error_details") + @ExcludeMissing + fun _pythonGraderRuntimeErrorDetails(): JsonField = + pythonGraderRuntimeErrorDetails + + /** + * Returns the raw JSON value of [pythonGraderServerError]. + * + * Unlike [pythonGraderServerError], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("python_grader_server_error") + @ExcludeMissing + fun _pythonGraderServerError(): JsonField = pythonGraderServerError + + /** + * Returns the raw JSON value of [pythonGraderServerErrorType]. + * + * Unlike [pythonGraderServerErrorType], this method doesn't throw if the JSON field has + * an unexpected type. + */ + @JsonProperty("python_grader_server_error_type") + @ExcludeMissing + fun _pythonGraderServerErrorType(): JsonField = pythonGraderServerErrorType + + /** + * Returns the raw JSON value of [sampleParseError]. + * + * Unlike [sampleParseError], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("sample_parse_error") + @ExcludeMissing + fun _sampleParseError(): JsonField = sampleParseError + + /** + * Returns the raw JSON value of [truncatedObservationError]. + * + * Unlike [truncatedObservationError], this method doesn't throw if the JSON field has + * an unexpected type. + */ + @JsonProperty("truncated_observation_error") + @ExcludeMissing + fun _truncatedObservationError(): JsonField = truncatedObservationError + + /** + * Returns the raw JSON value of [unresponsiveRewardError]. + * + * Unlike [unresponsiveRewardError], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("unresponsive_reward_error") + @ExcludeMissing + fun _unresponsiveRewardError(): JsonField = unresponsiveRewardError + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Errors]. + * + * The following fields are required: + * ```java + * .formulaParseError() + * .invalidVariableError() + * .modelGraderParseError() + * .modelGraderRefusalError() + * .modelGraderServerError() + * .modelGraderServerErrorDetails() + * .otherError() + * .pythonGraderRuntimeError() + * .pythonGraderRuntimeErrorDetails() + * .pythonGraderServerError() + * .pythonGraderServerErrorType() + * .sampleParseError() + * .truncatedObservationError() + * .unresponsiveRewardError() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Errors]. */ + class Builder internal constructor() { + + private var formulaParseError: JsonField? = null + private var invalidVariableError: JsonField? = null + private var modelGraderParseError: JsonField? = null + private var modelGraderRefusalError: JsonField? = null + private var modelGraderServerError: JsonField? = null + private var modelGraderServerErrorDetails: JsonField? = null + private var otherError: JsonField? = null + private var pythonGraderRuntimeError: JsonField? = null + private var pythonGraderRuntimeErrorDetails: JsonField? = null + private var pythonGraderServerError: JsonField? = null + private var pythonGraderServerErrorType: JsonField? = null + private var sampleParseError: JsonField? = null + private var truncatedObservationError: JsonField? = null + private var unresponsiveRewardError: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(errors: Errors) = apply { + formulaParseError = errors.formulaParseError + invalidVariableError = errors.invalidVariableError + modelGraderParseError = errors.modelGraderParseError + modelGraderRefusalError = errors.modelGraderRefusalError + modelGraderServerError = errors.modelGraderServerError + modelGraderServerErrorDetails = errors.modelGraderServerErrorDetails + otherError = errors.otherError + pythonGraderRuntimeError = errors.pythonGraderRuntimeError + pythonGraderRuntimeErrorDetails = errors.pythonGraderRuntimeErrorDetails + pythonGraderServerError = errors.pythonGraderServerError + pythonGraderServerErrorType = errors.pythonGraderServerErrorType + sampleParseError = errors.sampleParseError + truncatedObservationError = errors.truncatedObservationError + unresponsiveRewardError = errors.unresponsiveRewardError + additionalProperties = errors.additionalProperties.toMutableMap() + } + + fun formulaParseError(formulaParseError: Boolean) = + formulaParseError(JsonField.of(formulaParseError)) + + /** + * Sets [Builder.formulaParseError] to an arbitrary JSON value. + * + * You should usually call [Builder.formulaParseError] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun formulaParseError(formulaParseError: JsonField) = apply { + this.formulaParseError = formulaParseError + } + + fun invalidVariableError(invalidVariableError: Boolean) = + invalidVariableError(JsonField.of(invalidVariableError)) + + /** + * Sets [Builder.invalidVariableError] to an arbitrary JSON value. + * + * You should usually call [Builder.invalidVariableError] with a well-typed + * [Boolean] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun invalidVariableError(invalidVariableError: JsonField) = apply { + this.invalidVariableError = invalidVariableError + } + + fun modelGraderParseError(modelGraderParseError: Boolean) = + modelGraderParseError(JsonField.of(modelGraderParseError)) + + /** + * Sets [Builder.modelGraderParseError] to an arbitrary JSON value. + * + * You should usually call [Builder.modelGraderParseError] with a well-typed + * [Boolean] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun modelGraderParseError(modelGraderParseError: JsonField) = apply { + this.modelGraderParseError = modelGraderParseError + } + + fun modelGraderRefusalError(modelGraderRefusalError: Boolean) = + modelGraderRefusalError(JsonField.of(modelGraderRefusalError)) + + /** + * Sets [Builder.modelGraderRefusalError] to an arbitrary JSON value. + * + * You should usually call [Builder.modelGraderRefusalError] with a well-typed + * [Boolean] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun modelGraderRefusalError(modelGraderRefusalError: JsonField) = apply { + this.modelGraderRefusalError = modelGraderRefusalError + } + + fun modelGraderServerError(modelGraderServerError: Boolean) = + modelGraderServerError(JsonField.of(modelGraderServerError)) + + /** + * Sets [Builder.modelGraderServerError] to an arbitrary JSON value. + * + * You should usually call [Builder.modelGraderServerError] with a well-typed + * [Boolean] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun modelGraderServerError(modelGraderServerError: JsonField) = apply { + this.modelGraderServerError = modelGraderServerError + } + + fun modelGraderServerErrorDetails(modelGraderServerErrorDetails: String?) = + modelGraderServerErrorDetails( + JsonField.ofNullable(modelGraderServerErrorDetails) + ) + + /** + * Alias for calling [Builder.modelGraderServerErrorDetails] with + * `modelGraderServerErrorDetails.orElse(null)`. + */ + fun modelGraderServerErrorDetails(modelGraderServerErrorDetails: Optional) = + modelGraderServerErrorDetails(modelGraderServerErrorDetails.getOrNull()) + + /** + * Sets [Builder.modelGraderServerErrorDetails] to an arbitrary JSON value. + * + * You should usually call [Builder.modelGraderServerErrorDetails] with a well-typed + * [String] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun modelGraderServerErrorDetails( + modelGraderServerErrorDetails: JsonField + ) = apply { this.modelGraderServerErrorDetails = modelGraderServerErrorDetails } + + fun otherError(otherError: Boolean) = otherError(JsonField.of(otherError)) + + /** + * Sets [Builder.otherError] to an arbitrary JSON value. + * + * You should usually call [Builder.otherError] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun otherError(otherError: JsonField) = apply { + this.otherError = otherError + } + + fun pythonGraderRuntimeError(pythonGraderRuntimeError: Boolean) = + pythonGraderRuntimeError(JsonField.of(pythonGraderRuntimeError)) + + /** + * Sets [Builder.pythonGraderRuntimeError] to an arbitrary JSON value. + * + * You should usually call [Builder.pythonGraderRuntimeError] with a well-typed + * [Boolean] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun pythonGraderRuntimeError(pythonGraderRuntimeError: JsonField) = apply { + this.pythonGraderRuntimeError = pythonGraderRuntimeError + } + + fun pythonGraderRuntimeErrorDetails(pythonGraderRuntimeErrorDetails: String?) = + pythonGraderRuntimeErrorDetails( + JsonField.ofNullable(pythonGraderRuntimeErrorDetails) + ) + + /** + * Alias for calling [Builder.pythonGraderRuntimeErrorDetails] with + * `pythonGraderRuntimeErrorDetails.orElse(null)`. + */ + fun pythonGraderRuntimeErrorDetails( + pythonGraderRuntimeErrorDetails: Optional + ) = pythonGraderRuntimeErrorDetails(pythonGraderRuntimeErrorDetails.getOrNull()) + + /** + * Sets [Builder.pythonGraderRuntimeErrorDetails] to an arbitrary JSON value. + * + * You should usually call [Builder.pythonGraderRuntimeErrorDetails] with a + * well-typed [String] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun pythonGraderRuntimeErrorDetails( + pythonGraderRuntimeErrorDetails: JsonField + ) = apply { this.pythonGraderRuntimeErrorDetails = pythonGraderRuntimeErrorDetails } + + fun pythonGraderServerError(pythonGraderServerError: Boolean) = + pythonGraderServerError(JsonField.of(pythonGraderServerError)) + + /** + * Sets [Builder.pythonGraderServerError] to an arbitrary JSON value. + * + * You should usually call [Builder.pythonGraderServerError] with a well-typed + * [Boolean] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun pythonGraderServerError(pythonGraderServerError: JsonField) = apply { + this.pythonGraderServerError = pythonGraderServerError + } + + fun pythonGraderServerErrorType(pythonGraderServerErrorType: String?) = + pythonGraderServerErrorType(JsonField.ofNullable(pythonGraderServerErrorType)) + + /** + * Alias for calling [Builder.pythonGraderServerErrorType] with + * `pythonGraderServerErrorType.orElse(null)`. + */ + fun pythonGraderServerErrorType(pythonGraderServerErrorType: Optional) = + pythonGraderServerErrorType(pythonGraderServerErrorType.getOrNull()) + + /** + * Sets [Builder.pythonGraderServerErrorType] to an arbitrary JSON value. + * + * You should usually call [Builder.pythonGraderServerErrorType] with a well-typed + * [String] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun pythonGraderServerErrorType(pythonGraderServerErrorType: JsonField) = + apply { + this.pythonGraderServerErrorType = pythonGraderServerErrorType + } + + fun sampleParseError(sampleParseError: Boolean) = + sampleParseError(JsonField.of(sampleParseError)) + + /** + * Sets [Builder.sampleParseError] to an arbitrary JSON value. + * + * You should usually call [Builder.sampleParseError] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun sampleParseError(sampleParseError: JsonField) = apply { + this.sampleParseError = sampleParseError + } + + fun truncatedObservationError(truncatedObservationError: Boolean) = + truncatedObservationError(JsonField.of(truncatedObservationError)) + + /** + * Sets [Builder.truncatedObservationError] to an arbitrary JSON value. + * + * You should usually call [Builder.truncatedObservationError] with a well-typed + * [Boolean] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun truncatedObservationError(truncatedObservationError: JsonField) = + apply { + this.truncatedObservationError = truncatedObservationError + } + + fun unresponsiveRewardError(unresponsiveRewardError: Boolean) = + unresponsiveRewardError(JsonField.of(unresponsiveRewardError)) + + /** + * Sets [Builder.unresponsiveRewardError] to an arbitrary JSON value. + * + * You should usually call [Builder.unresponsiveRewardError] with a well-typed + * [Boolean] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun unresponsiveRewardError(unresponsiveRewardError: JsonField) = apply { + this.unresponsiveRewardError = unresponsiveRewardError + } + + 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) + } + + /** + * Returns an immutable instance of [Errors]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .formulaParseError() + * .invalidVariableError() + * .modelGraderParseError() + * .modelGraderRefusalError() + * .modelGraderServerError() + * .modelGraderServerErrorDetails() + * .otherError() + * .pythonGraderRuntimeError() + * .pythonGraderRuntimeErrorDetails() + * .pythonGraderServerError() + * .pythonGraderServerErrorType() + * .sampleParseError() + * .truncatedObservationError() + * .unresponsiveRewardError() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Errors = + Errors( + checkRequired("formulaParseError", formulaParseError), + checkRequired("invalidVariableError", invalidVariableError), + checkRequired("modelGraderParseError", modelGraderParseError), + checkRequired("modelGraderRefusalError", modelGraderRefusalError), + checkRequired("modelGraderServerError", modelGraderServerError), + checkRequired( + "modelGraderServerErrorDetails", + modelGraderServerErrorDetails, + ), + checkRequired("otherError", otherError), + checkRequired("pythonGraderRuntimeError", pythonGraderRuntimeError), + checkRequired( + "pythonGraderRuntimeErrorDetails", + pythonGraderRuntimeErrorDetails, + ), + checkRequired("pythonGraderServerError", pythonGraderServerError), + checkRequired("pythonGraderServerErrorType", pythonGraderServerErrorType), + checkRequired("sampleParseError", sampleParseError), + checkRequired("truncatedObservationError", truncatedObservationError), + checkRequired("unresponsiveRewardError", unresponsiveRewardError), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Errors = apply { + if (validated) { + return@apply + } + + formulaParseError() + invalidVariableError() + modelGraderParseError() + modelGraderRefusalError() + modelGraderServerError() + modelGraderServerErrorDetails() + otherError() + pythonGraderRuntimeError() + pythonGraderRuntimeErrorDetails() + pythonGraderServerError() + pythonGraderServerErrorType() + sampleParseError() + truncatedObservationError() + unresponsiveRewardError() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (formulaParseError.asKnown().isPresent) 1 else 0) + + (if (invalidVariableError.asKnown().isPresent) 1 else 0) + + (if (modelGraderParseError.asKnown().isPresent) 1 else 0) + + (if (modelGraderRefusalError.asKnown().isPresent) 1 else 0) + + (if (modelGraderServerError.asKnown().isPresent) 1 else 0) + + (if (modelGraderServerErrorDetails.asKnown().isPresent) 1 else 0) + + (if (otherError.asKnown().isPresent) 1 else 0) + + (if (pythonGraderRuntimeError.asKnown().isPresent) 1 else 0) + + (if (pythonGraderRuntimeErrorDetails.asKnown().isPresent) 1 else 0) + + (if (pythonGraderServerError.asKnown().isPresent) 1 else 0) + + (if (pythonGraderServerErrorType.asKnown().isPresent) 1 else 0) + + (if (sampleParseError.asKnown().isPresent) 1 else 0) + + (if (truncatedObservationError.asKnown().isPresent) 1 else 0) + + (if (unresponsiveRewardError.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Errors && formulaParseError == other.formulaParseError && invalidVariableError == other.invalidVariableError && modelGraderParseError == other.modelGraderParseError && modelGraderRefusalError == other.modelGraderRefusalError && modelGraderServerError == other.modelGraderServerError && modelGraderServerErrorDetails == other.modelGraderServerErrorDetails && otherError == other.otherError && pythonGraderRuntimeError == other.pythonGraderRuntimeError && pythonGraderRuntimeErrorDetails == other.pythonGraderRuntimeErrorDetails && pythonGraderServerError == other.pythonGraderServerError && pythonGraderServerErrorType == other.pythonGraderServerErrorType && sampleParseError == other.sampleParseError && truncatedObservationError == other.truncatedObservationError && unresponsiveRewardError == other.unresponsiveRewardError && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(formulaParseError, invalidVariableError, modelGraderParseError, modelGraderRefusalError, modelGraderServerError, modelGraderServerErrorDetails, otherError, pythonGraderRuntimeError, pythonGraderRuntimeErrorDetails, pythonGraderServerError, pythonGraderServerErrorType, sampleParseError, truncatedObservationError, unresponsiveRewardError, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Errors{formulaParseError=$formulaParseError, invalidVariableError=$invalidVariableError, modelGraderParseError=$modelGraderParseError, modelGraderRefusalError=$modelGraderRefusalError, modelGraderServerError=$modelGraderServerError, modelGraderServerErrorDetails=$modelGraderServerErrorDetails, otherError=$otherError, pythonGraderRuntimeError=$pythonGraderRuntimeError, pythonGraderRuntimeErrorDetails=$pythonGraderRuntimeErrorDetails, pythonGraderServerError=$pythonGraderServerError, pythonGraderServerErrorType=$pythonGraderServerErrorType, sampleParseError=$sampleParseError, truncatedObservationError=$truncatedObservationError, unresponsiveRewardError=$unresponsiveRewardError, additionalProperties=$additionalProperties}" + } + + class Scores + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Scores]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Scores]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(scores: Scores) = apply { + additionalProperties = scores.additionalProperties.toMutableMap() + } + + 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) + } + + /** + * Returns an immutable instance of [Scores]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Scores = Scores(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): Scores = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Scores && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Scores{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Metadata && errors == other.errors && executionTime == other.executionTime && name == other.name && sampledModelName == other.sampledModelName && scores == other.scores && tokenUsage == other.tokenUsage && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(errors, executionTime, name, sampledModelName, scores, tokenUsage, type, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Metadata{errors=$errors, executionTime=$executionTime, name=$name, sampledModelName=$sampledModelName, scores=$scores, tokenUsage=$tokenUsage, type=$type, additionalProperties=$additionalProperties}" + } + + class ModelGraderTokenUsagePerModel + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [ModelGraderTokenUsagePerModel]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ModelGraderTokenUsagePerModel]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(modelGraderTokenUsagePerModel: ModelGraderTokenUsagePerModel) = + apply { + additionalProperties = + modelGraderTokenUsagePerModel.additionalProperties.toMutableMap() + } + + 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) + } + + /** + * Returns an immutable instance of [ModelGraderTokenUsagePerModel]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ModelGraderTokenUsagePerModel = + ModelGraderTokenUsagePerModel(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): ModelGraderTokenUsagePerModel = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is ModelGraderTokenUsagePerModel && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ModelGraderTokenUsagePerModel{additionalProperties=$additionalProperties}" + } + + class SubRewards + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [SubRewards]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [SubRewards]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(subRewards: SubRewards) = apply { + additionalProperties = subRewards.additionalProperties.toMutableMap() + } + + 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) + } + + /** + * Returns an immutable instance of [SubRewards]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): SubRewards = SubRewards(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): SubRewards = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is SubRewards && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "SubRewards{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is GraderRunResponse && metadata == other.metadata && modelGraderTokenUsagePerModel == other.modelGraderTokenUsagePerModel && reward == other.reward && subRewards == other.subRewards && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(metadata, modelGraderTokenUsagePerModel, reward, subRewards, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "GraderRunResponse{metadata=$metadata, modelGraderTokenUsagePerModel=$modelGraderTokenUsagePerModel, reward=$reward, subRewards=$subRewards, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateParams.kt new file mode 100644 index 00000000..329bf9cb --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateParams.kt @@ -0,0 +1,747 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.alpha.graders + +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.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.ExcludeMissing +import com.openai.core.JsonField +import com.openai.core.JsonMissing +import com.openai.core.JsonValue +import com.openai.core.Params +import com.openai.core.allMaxBy +import com.openai.core.checkRequired +import com.openai.core.getOrThrow +import com.openai.core.http.Headers +import com.openai.core.http.QueryParams +import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.graders.gradermodels.MultiGrader +import com.openai.models.graders.gradermodels.PythonGrader +import com.openai.models.graders.gradermodels.ScoreModelGrader +import com.openai.models.graders.gradermodels.StringCheckGrader +import com.openai.models.graders.gradermodels.TextSimilarityGrader +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** Validate a grader. */ +class GraderValidateParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * The grader used for the fine-tuning job. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun grader(): Grader = body.grader() + + /** + * Returns the raw JSON value of [grader]. + * + * Unlike [grader], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _grader(): JsonField = body._grader() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [GraderValidateParams]. + * + * The following fields are required: + * ```java + * .grader() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [GraderValidateParams]. */ + 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(graderValidateParams: GraderValidateParams) = apply { + body = graderValidateParams.body.toBuilder() + additionalHeaders = graderValidateParams.additionalHeaders.toBuilder() + additionalQueryParams = graderValidateParams.additionalQueryParams.toBuilder() + } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [grader] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** The grader used for the fine-tuning job. */ + fun grader(grader: Grader) = apply { body.grader(grader) } + + /** + * Sets [Builder.grader] to an arbitrary JSON value. + * + * You should usually call [Builder.grader] with a well-typed [Grader] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun grader(grader: JsonField) = apply { body.grader(grader) } + + /** Alias for calling [grader] with `Grader.ofStringCheck(stringCheck)`. */ + fun grader(stringCheck: StringCheckGrader) = apply { body.grader(stringCheck) } + + /** Alias for calling [grader] with `Grader.ofTextSimilarity(textSimilarity)`. */ + fun grader(textSimilarity: TextSimilarityGrader) = apply { body.grader(textSimilarity) } + + /** Alias for calling [grader] with `Grader.ofPython(python)`. */ + fun grader(python: PythonGrader) = apply { body.grader(python) } + + /** Alias for calling [grader] with `Grader.ofScoreModel(scoreModel)`. */ + fun grader(scoreModel: ScoreModelGrader) = apply { body.grader(scoreModel) } + + /** Alias for calling [grader] with `Grader.ofMulti(multi)`. */ + fun grader(multi: MultiGrader) = apply { body.grader(multi) } + + 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) + } + + /** + * Returns an immutable instance of [GraderValidateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .grader() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): GraderValidateParams = + GraderValidateParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + private constructor( + private val grader: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("grader") @ExcludeMissing grader: JsonField = JsonMissing.of() + ) : this(grader, mutableMapOf()) + + /** + * The grader used for the fine-tuning job. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun grader(): Grader = grader.getRequired("grader") + + /** + * Returns the raw JSON value of [grader]. + * + * Unlike [grader], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("grader") @ExcludeMissing fun _grader(): JsonField = grader + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```java + * .grader() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var grader: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + grader = body.grader + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** The grader used for the fine-tuning job. */ + fun grader(grader: Grader) = grader(JsonField.of(grader)) + + /** + * Sets [Builder.grader] to an arbitrary JSON value. + * + * You should usually call [Builder.grader] with a well-typed [Grader] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun grader(grader: JsonField) = apply { this.grader = grader } + + /** Alias for calling [grader] with `Grader.ofStringCheck(stringCheck)`. */ + fun grader(stringCheck: StringCheckGrader) = grader(Grader.ofStringCheck(stringCheck)) + + /** Alias for calling [grader] with `Grader.ofTextSimilarity(textSimilarity)`. */ + fun grader(textSimilarity: TextSimilarityGrader) = + grader(Grader.ofTextSimilarity(textSimilarity)) + + /** Alias for calling [grader] with `Grader.ofPython(python)`. */ + fun grader(python: PythonGrader) = grader(Grader.ofPython(python)) + + /** Alias for calling [grader] with `Grader.ofScoreModel(scoreModel)`. */ + fun grader(scoreModel: ScoreModelGrader) = grader(Grader.ofScoreModel(scoreModel)) + + /** Alias for calling [grader] with `Grader.ofMulti(multi)`. */ + fun grader(multi: MultiGrader) = grader(Grader.ofMulti(multi)) + + 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) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .grader() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body(checkRequired("grader", grader), additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + grader().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = (grader.asKnown().getOrNull()?.validity() ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && grader == other.grader && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(grader, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Body{grader=$grader, additionalProperties=$additionalProperties}" + } + + /** The grader used for the fine-tuning job. */ + @JsonDeserialize(using = Grader.Deserializer::class) + @JsonSerialize(using = Grader.Serializer::class) + class Grader + private constructor( + private val stringCheck: StringCheckGrader? = null, + private val textSimilarity: TextSimilarityGrader? = null, + private val python: PythonGrader? = null, + private val scoreModel: ScoreModelGrader? = null, + private val multi: MultiGrader? = null, + private val _json: JsonValue? = null, + ) { + + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun stringCheck(): Optional = Optional.ofNullable(stringCheck) + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun textSimilarity(): Optional = Optional.ofNullable(textSimilarity) + + /** A PythonGrader object that runs a python script on the input. */ + fun python(): Optional = Optional.ofNullable(python) + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun scoreModel(): Optional = Optional.ofNullable(scoreModel) + + /** + * A MultiGrader object combines the output of multiple graders to produce a single score. + */ + fun multi(): Optional = Optional.ofNullable(multi) + + fun isStringCheck(): Boolean = stringCheck != null + + fun isTextSimilarity(): Boolean = textSimilarity != null + + fun isPython(): Boolean = python != null + + fun isScoreModel(): Boolean = scoreModel != null + + fun isMulti(): Boolean = multi != null + + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun asStringCheck(): StringCheckGrader = stringCheck.getOrThrow("stringCheck") + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun asTextSimilarity(): TextSimilarityGrader = textSimilarity.getOrThrow("textSimilarity") + + /** A PythonGrader object that runs a python script on the input. */ + fun asPython(): PythonGrader = python.getOrThrow("python") + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun asScoreModel(): ScoreModelGrader = scoreModel.getOrThrow("scoreModel") + + /** + * A MultiGrader object combines the output of multiple graders to produce a single score. + */ + fun asMulti(): MultiGrader = multi.getOrThrow("multi") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + stringCheck != null -> visitor.visitStringCheck(stringCheck) + textSimilarity != null -> visitor.visitTextSimilarity(textSimilarity) + python != null -> visitor.visitPython(python) + scoreModel != null -> visitor.visitScoreModel(scoreModel) + multi != null -> visitor.visitMulti(multi) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Grader = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitStringCheck(stringCheck: StringCheckGrader) { + stringCheck.validate() + } + + override fun visitTextSimilarity(textSimilarity: TextSimilarityGrader) { + textSimilarity.validate() + } + + override fun visitPython(python: PythonGrader) { + python.validate() + } + + override fun visitScoreModel(scoreModel: ScoreModelGrader) { + scoreModel.validate() + } + + override fun visitMulti(multi: MultiGrader) { + multi.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitStringCheck(stringCheck: StringCheckGrader) = + stringCheck.validity() + + override fun visitTextSimilarity(textSimilarity: TextSimilarityGrader) = + textSimilarity.validity() + + override fun visitPython(python: PythonGrader) = python.validity() + + override fun visitScoreModel(scoreModel: ScoreModelGrader) = + scoreModel.validity() + + override fun visitMulti(multi: MultiGrader) = multi.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Grader && stringCheck == other.stringCheck && textSimilarity == other.textSimilarity && python == other.python && scoreModel == other.scoreModel && multi == other.multi /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(stringCheck, textSimilarity, python, scoreModel, multi) /* spotless:on */ + + override fun toString(): String = + when { + stringCheck != null -> "Grader{stringCheck=$stringCheck}" + textSimilarity != null -> "Grader{textSimilarity=$textSimilarity}" + python != null -> "Grader{python=$python}" + scoreModel != null -> "Grader{scoreModel=$scoreModel}" + multi != null -> "Grader{multi=$multi}" + _json != null -> "Grader{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Grader") + } + + companion object { + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + @JvmStatic + fun ofStringCheck(stringCheck: StringCheckGrader) = Grader(stringCheck = stringCheck) + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + @JvmStatic + fun ofTextSimilarity(textSimilarity: TextSimilarityGrader) = + Grader(textSimilarity = textSimilarity) + + /** A PythonGrader object that runs a python script on the input. */ + @JvmStatic fun ofPython(python: PythonGrader) = Grader(python = python) + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + @JvmStatic + fun ofScoreModel(scoreModel: ScoreModelGrader) = Grader(scoreModel = scoreModel) + + /** + * A MultiGrader object combines the output of multiple graders to produce a single + * score. + */ + @JvmStatic fun ofMulti(multi: MultiGrader) = Grader(multi = multi) + } + + /** An interface that defines how to map each variant of [Grader] to a value of type [T]. */ + interface Visitor { + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + fun visitStringCheck(stringCheck: StringCheckGrader): T + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun visitTextSimilarity(textSimilarity: TextSimilarityGrader): T + + /** A PythonGrader object that runs a python script on the input. */ + fun visitPython(python: PythonGrader): T + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun visitScoreModel(scoreModel: ScoreModelGrader): T + + /** + * A MultiGrader object combines the output of multiple graders to produce a single + * score. + */ + fun visitMulti(multi: MultiGrader): T + + /** + * Maps an unknown variant of [Grader] to a value of type [T]. + * + * An instance of [Grader] 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 Grader: $json") + } + } + + internal class Deserializer : BaseDeserializer(Grader::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Grader { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Grader(stringCheck = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Grader(textSimilarity = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Grader(python = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Grader(scoreModel = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Grader(multi = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> Grader(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Grader::class) { + + override fun serialize( + value: Grader, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.stringCheck != null -> generator.writeObject(value.stringCheck) + value.textSimilarity != null -> generator.writeObject(value.textSimilarity) + value.python != null -> generator.writeObject(value.python) + value.scoreModel != null -> generator.writeObject(value.scoreModel) + value.multi != null -> generator.writeObject(value.multi) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Grader") + } + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is GraderValidateParams && 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() = + "GraderValidateParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateResponse.kt new file mode 100644 index 00000000..c3229eb5 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateResponse.kt @@ -0,0 +1,478 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.alpha.graders + +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.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.ExcludeMissing +import com.openai.core.JsonField +import com.openai.core.JsonMissing +import com.openai.core.JsonValue +import com.openai.core.allMaxBy +import com.openai.core.getOrThrow +import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.graders.gradermodels.MultiGrader +import com.openai.models.graders.gradermodels.PythonGrader +import com.openai.models.graders.gradermodels.ScoreModelGrader +import com.openai.models.graders.gradermodels.StringCheckGrader +import com.openai.models.graders.gradermodels.TextSimilarityGrader +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class GraderValidateResponse +private constructor( + private val grader: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("grader") @ExcludeMissing grader: JsonField = JsonMissing.of() + ) : this(grader, mutableMapOf()) + + /** + * The grader used for the fine-tuning job. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun grader(): Optional = grader.getOptional("grader") + + /** + * Returns the raw JSON value of [grader]. + * + * Unlike [grader], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("grader") @ExcludeMissing fun _grader(): JsonField = grader + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [GraderValidateResponse]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [GraderValidateResponse]. */ + class Builder internal constructor() { + + private var grader: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(graderValidateResponse: GraderValidateResponse) = apply { + grader = graderValidateResponse.grader + additionalProperties = graderValidateResponse.additionalProperties.toMutableMap() + } + + /** The grader used for the fine-tuning job. */ + fun grader(grader: Grader) = grader(JsonField.of(grader)) + + /** + * Sets [Builder.grader] to an arbitrary JSON value. + * + * You should usually call [Builder.grader] with a well-typed [Grader] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun grader(grader: JsonField) = apply { this.grader = grader } + + /** Alias for calling [grader] with `Grader.ofStringCheck(stringCheck)`. */ + fun grader(stringCheck: StringCheckGrader) = grader(Grader.ofStringCheck(stringCheck)) + + /** Alias for calling [grader] with `Grader.ofTextSimilarity(textSimilarity)`. */ + fun grader(textSimilarity: TextSimilarityGrader) = + grader(Grader.ofTextSimilarity(textSimilarity)) + + /** Alias for calling [grader] with `Grader.ofPython(python)`. */ + fun grader(python: PythonGrader) = grader(Grader.ofPython(python)) + + /** Alias for calling [grader] with `Grader.ofScoreModel(scoreModel)`. */ + fun grader(scoreModel: ScoreModelGrader) = grader(Grader.ofScoreModel(scoreModel)) + + /** Alias for calling [grader] with `Grader.ofMulti(multi)`. */ + fun grader(multi: MultiGrader) = grader(Grader.ofMulti(multi)) + + 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) + } + + /** + * Returns an immutable instance of [GraderValidateResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): GraderValidateResponse = + GraderValidateResponse(grader, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): GraderValidateResponse = apply { + if (validated) { + return@apply + } + + grader().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = (grader.asKnown().getOrNull()?.validity() ?: 0) + + /** The grader used for the fine-tuning job. */ + @JsonDeserialize(using = Grader.Deserializer::class) + @JsonSerialize(using = Grader.Serializer::class) + class Grader + private constructor( + private val stringCheck: StringCheckGrader? = null, + private val textSimilarity: TextSimilarityGrader? = null, + private val python: PythonGrader? = null, + private val scoreModel: ScoreModelGrader? = null, + private val multi: MultiGrader? = null, + private val _json: JsonValue? = null, + ) { + + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun stringCheck(): Optional = Optional.ofNullable(stringCheck) + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun textSimilarity(): Optional = Optional.ofNullable(textSimilarity) + + /** A PythonGrader object that runs a python script on the input. */ + fun python(): Optional = Optional.ofNullable(python) + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun scoreModel(): Optional = Optional.ofNullable(scoreModel) + + /** + * A MultiGrader object combines the output of multiple graders to produce a single score. + */ + fun multi(): Optional = Optional.ofNullable(multi) + + fun isStringCheck(): Boolean = stringCheck != null + + fun isTextSimilarity(): Boolean = textSimilarity != null + + fun isPython(): Boolean = python != null + + fun isScoreModel(): Boolean = scoreModel != null + + fun isMulti(): Boolean = multi != null + + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun asStringCheck(): StringCheckGrader = stringCheck.getOrThrow("stringCheck") + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun asTextSimilarity(): TextSimilarityGrader = textSimilarity.getOrThrow("textSimilarity") + + /** A PythonGrader object that runs a python script on the input. */ + fun asPython(): PythonGrader = python.getOrThrow("python") + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun asScoreModel(): ScoreModelGrader = scoreModel.getOrThrow("scoreModel") + + /** + * A MultiGrader object combines the output of multiple graders to produce a single score. + */ + fun asMulti(): MultiGrader = multi.getOrThrow("multi") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + stringCheck != null -> visitor.visitStringCheck(stringCheck) + textSimilarity != null -> visitor.visitTextSimilarity(textSimilarity) + python != null -> visitor.visitPython(python) + scoreModel != null -> visitor.visitScoreModel(scoreModel) + multi != null -> visitor.visitMulti(multi) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Grader = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitStringCheck(stringCheck: StringCheckGrader) { + stringCheck.validate() + } + + override fun visitTextSimilarity(textSimilarity: TextSimilarityGrader) { + textSimilarity.validate() + } + + override fun visitPython(python: PythonGrader) { + python.validate() + } + + override fun visitScoreModel(scoreModel: ScoreModelGrader) { + scoreModel.validate() + } + + override fun visitMulti(multi: MultiGrader) { + multi.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitStringCheck(stringCheck: StringCheckGrader) = + stringCheck.validity() + + override fun visitTextSimilarity(textSimilarity: TextSimilarityGrader) = + textSimilarity.validity() + + override fun visitPython(python: PythonGrader) = python.validity() + + override fun visitScoreModel(scoreModel: ScoreModelGrader) = + scoreModel.validity() + + override fun visitMulti(multi: MultiGrader) = multi.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Grader && stringCheck == other.stringCheck && textSimilarity == other.textSimilarity && python == other.python && scoreModel == other.scoreModel && multi == other.multi /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(stringCheck, textSimilarity, python, scoreModel, multi) /* spotless:on */ + + override fun toString(): String = + when { + stringCheck != null -> "Grader{stringCheck=$stringCheck}" + textSimilarity != null -> "Grader{textSimilarity=$textSimilarity}" + python != null -> "Grader{python=$python}" + scoreModel != null -> "Grader{scoreModel=$scoreModel}" + multi != null -> "Grader{multi=$multi}" + _json != null -> "Grader{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Grader") + } + + companion object { + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + @JvmStatic + fun ofStringCheck(stringCheck: StringCheckGrader) = Grader(stringCheck = stringCheck) + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + @JvmStatic + fun ofTextSimilarity(textSimilarity: TextSimilarityGrader) = + Grader(textSimilarity = textSimilarity) + + /** A PythonGrader object that runs a python script on the input. */ + @JvmStatic fun ofPython(python: PythonGrader) = Grader(python = python) + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + @JvmStatic + fun ofScoreModel(scoreModel: ScoreModelGrader) = Grader(scoreModel = scoreModel) + + /** + * A MultiGrader object combines the output of multiple graders to produce a single + * score. + */ + @JvmStatic fun ofMulti(multi: MultiGrader) = Grader(multi = multi) + } + + /** An interface that defines how to map each variant of [Grader] to a value of type [T]. */ + interface Visitor { + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + fun visitStringCheck(stringCheck: StringCheckGrader): T + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun visitTextSimilarity(textSimilarity: TextSimilarityGrader): T + + /** A PythonGrader object that runs a python script on the input. */ + fun visitPython(python: PythonGrader): T + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun visitScoreModel(scoreModel: ScoreModelGrader): T + + /** + * A MultiGrader object combines the output of multiple graders to produce a single + * score. + */ + fun visitMulti(multi: MultiGrader): T + + /** + * Maps an unknown variant of [Grader] to a value of type [T]. + * + * An instance of [Grader] 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 Grader: $json") + } + } + + internal class Deserializer : BaseDeserializer(Grader::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Grader { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Grader(stringCheck = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Grader(textSimilarity = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Grader(python = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Grader(scoreModel = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Grader(multi = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> Grader(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Grader::class) { + + override fun serialize( + value: Grader, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.stringCheck != null -> generator.writeObject(value.stringCheck) + value.textSimilarity != null -> generator.writeObject(value.textSimilarity) + value.python != null -> generator.writeObject(value.python) + value.scoreModel != null -> generator.writeObject(value.scoreModel) + value.multi != null -> generator.writeObject(value.multi) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Grader") + } + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is GraderValidateResponse && grader == other.grader && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(grader, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "GraderValidateResponse{grader=$grader, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionCreatePage.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionCreatePage.kt index ea6aeac9..fd72758d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionCreatePage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionCreatePage.kt @@ -2,13 +2,12 @@ package com.openai.models.finetuning.checkpoints.permissions +import com.openai.core.AutoPager import com.openai.core.JsonValue +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.finetuning.checkpoints.PermissionService import java.util.Objects -import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [PermissionService.create] */ @@ -17,7 +16,7 @@ private constructor( private val service: PermissionService, private val params: PermissionCreateParams, private val response: PermissionCreatePageResponse, -) { +) : Page { /** * Delegates to [PermissionCreatePageResponse], but gracefully handles missing data. @@ -30,14 +29,16 @@ private constructor( /** @see [PermissionCreatePageResponse.object_] */ fun object_(): JsonValue = response._object_() - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional = Optional.empty() + override fun hasNextPage(): Boolean = items().isNotEmpty() - fun getNextPage(): Optional = - getNextPageParams().map { service.create(it) } + fun nextPageParams(): PermissionCreateParams = + throw IllegalStateException("Cannot construct next page params") - fun autoPager(): AutoPager = AutoPager(this) + override fun nextPage(): PermissionCreatePage = service.create(nextPageParams()) + + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): PermissionCreateParams = params @@ -106,26 +107,6 @@ private constructor( ) } - class AutoPager(private val firstPage: PermissionCreatePage) : - Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionCreatePageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionCreatePageAsync.kt index 174dc4c7..c67b8a80 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionCreatePageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionCreatePageAsync.kt @@ -2,23 +2,24 @@ package com.openai.models.finetuning.checkpoints.permissions +import com.openai.core.AutoPagerAsync import com.openai.core.JsonValue +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.finetuning.checkpoints.PermissionServiceAsync import java.util.Objects -import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [PermissionServiceAsync.create] */ class PermissionCreatePageAsync private constructor( private val service: PermissionServiceAsync, + private val streamHandlerExecutor: Executor, private val params: PermissionCreateParams, private val response: PermissionCreatePageResponse, -) { +) : PageAsync { /** * Delegates to [PermissionCreatePageResponse], but gracefully handles missing data. @@ -31,16 +32,18 @@ private constructor( /** @see [PermissionCreatePageResponse.object_] */ fun object_(): JsonValue = response._object_() - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional = Optional.empty() + override fun hasNextPage(): Boolean = items().isNotEmpty() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.create(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + fun nextPageParams(): PermissionCreateParams = + throw IllegalStateException("Cannot construct next page params") - fun autoPager(): AutoPager = AutoPager(this) + override fun nextPage(): CompletableFuture = + service.create(nextPageParams()) + + fun autoPager(): AutoPagerAsync = + AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): PermissionCreateParams = params @@ -58,6 +61,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -69,18 +73,24 @@ private constructor( class Builder internal constructor() { private var service: PermissionServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: PermissionCreateParams? = null private var response: PermissionCreatePageResponse? = null @JvmSynthetic internal fun from(permissionCreatePageAsync: PermissionCreatePageAsync) = apply { service = permissionCreatePageAsync.service + streamHandlerExecutor = permissionCreatePageAsync.streamHandlerExecutor params = permissionCreatePageAsync.params response = permissionCreatePageAsync.response } fun service(service: PermissionServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: PermissionCreateParams) = apply { this.params = params } @@ -95,6 +105,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -104,50 +115,22 @@ private constructor( fun build(): PermissionCreatePageAsync = PermissionCreatePageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: PermissionCreatePageAsync) { - - fun forEach( - action: Predicate, - executor: Executor, - ): CompletableFuture { - fun CompletableFuture>.forEach( - action: (PermissionCreateResponse) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is PermissionCreatePageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is PermissionCreatePageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "PermissionCreatePageAsync{service=$service, params=$params, response=$response}" + "PermissionCreatePageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionCreateParams.kt index 6346ac32..dcbd6ccd 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionCreateParams.kt @@ -19,6 +19,7 @@ import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects +import java.util.Optional import kotlin.jvm.optionals.getOrNull /** @@ -29,13 +30,13 @@ import kotlin.jvm.optionals.getOrNull */ class PermissionCreateParams private constructor( - private val fineTunedModelCheckpoint: String, + private val fineTunedModelCheckpoint: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun fineTunedModelCheckpoint(): String = fineTunedModelCheckpoint + fun fineTunedModelCheckpoint(): Optional = Optional.ofNullable(fineTunedModelCheckpoint) /** * The project identifiers to grant access to. @@ -67,7 +68,6 @@ private constructor( * * The following fields are required: * ```java - * .fineTunedModelCheckpoint() * .projectIds() * ``` */ @@ -90,10 +90,17 @@ private constructor( additionalQueryParams = permissionCreateParams.additionalQueryParams.toBuilder() } - fun fineTunedModelCheckpoint(fineTunedModelCheckpoint: String) = apply { + fun fineTunedModelCheckpoint(fineTunedModelCheckpoint: String?) = apply { this.fineTunedModelCheckpoint = fineTunedModelCheckpoint } + /** + * Alias for calling [Builder.fineTunedModelCheckpoint] with + * `fineTunedModelCheckpoint.orElse(null)`. + */ + fun fineTunedModelCheckpoint(fineTunedModelCheckpoint: Optional) = + fineTunedModelCheckpoint(fineTunedModelCheckpoint.getOrNull()) + /** * Sets the entire request body. * @@ -246,7 +253,6 @@ private constructor( * * The following fields are required: * ```java - * .fineTunedModelCheckpoint() * .projectIds() * ``` * @@ -254,7 +260,7 @@ private constructor( */ fun build(): PermissionCreateParams = PermissionCreateParams( - checkRequired("fineTunedModelCheckpoint", fineTunedModelCheckpoint), + fineTunedModelCheckpoint, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -265,7 +271,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> fineTunedModelCheckpoint + 0 -> fineTunedModelCheckpoint ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionDeleteParams.kt index bf8173c5..ce09abfc 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionDeleteParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionDeleteParams.kt @@ -10,6 +10,7 @@ import com.openai.core.http.QueryParams import com.openai.core.toImmutable import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * **NOTE:** This endpoint requires an [admin API key](../admin-api-keys). @@ -20,7 +21,7 @@ import java.util.Optional class PermissionDeleteParams private constructor( private val fineTunedModelCheckpoint: String, - private val permissionId: String, + private val permissionId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, @@ -28,7 +29,7 @@ private constructor( fun fineTunedModelCheckpoint(): String = fineTunedModelCheckpoint - fun permissionId(): String = permissionId + fun permissionId(): Optional = Optional.ofNullable(permissionId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -46,7 +47,6 @@ private constructor( * The following fields are required: * ```java * .fineTunedModelCheckpoint() - * .permissionId() * ``` */ @JvmStatic fun builder() = Builder() @@ -75,7 +75,10 @@ private constructor( this.fineTunedModelCheckpoint = fineTunedModelCheckpoint } - fun permissionId(permissionId: String) = apply { this.permissionId = permissionId } + fun permissionId(permissionId: String?) = apply { this.permissionId = permissionId } + + /** Alias for calling [Builder.permissionId] with `permissionId.orElse(null)`. */ + fun permissionId(permissionId: Optional) = permissionId(permissionId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -205,7 +208,6 @@ private constructor( * The following fields are required: * ```java * .fineTunedModelCheckpoint() - * .permissionId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -213,7 +215,7 @@ private constructor( fun build(): PermissionDeleteParams = PermissionDeleteParams( checkRequired("fineTunedModelCheckpoint", fineTunedModelCheckpoint), - checkRequired("permissionId", permissionId), + permissionId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -226,7 +228,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> fineTunedModelCheckpoint - 1 -> permissionId + 1 -> permissionId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionRetrieveParams.kt index e9276fee..2205d767 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionRetrieveParams.kt @@ -6,7 +6,6 @@ import com.fasterxml.jackson.annotation.JsonCreator import com.openai.core.Enum import com.openai.core.JsonField 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.errors.OpenAIInvalidDataException @@ -22,7 +21,7 @@ import kotlin.jvm.optionals.getOrNull */ class PermissionRetrieveParams private constructor( - private val fineTunedModelCheckpoint: String, + private val fineTunedModelCheckpoint: String?, private val after: String?, private val limit: Long?, private val order: Order?, @@ -31,7 +30,7 @@ private constructor( private val additionalQueryParams: QueryParams, ) : Params { - fun fineTunedModelCheckpoint(): String = fineTunedModelCheckpoint + fun fineTunedModelCheckpoint(): Optional = Optional.ofNullable(fineTunedModelCheckpoint) /** Identifier for the last permission ID from the previous pagination request. */ fun after(): Optional = Optional.ofNullable(after) @@ -53,14 +52,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [PermissionRetrieveParams]. - * - * The following fields are required: - * ```java - * .fineTunedModelCheckpoint() - * ``` - */ + @JvmStatic fun none(): PermissionRetrieveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [PermissionRetrieveParams]. */ @JvmStatic fun builder() = Builder() } @@ -86,10 +80,17 @@ private constructor( additionalQueryParams = permissionRetrieveParams.additionalQueryParams.toBuilder() } - fun fineTunedModelCheckpoint(fineTunedModelCheckpoint: String) = apply { + fun fineTunedModelCheckpoint(fineTunedModelCheckpoint: String?) = apply { this.fineTunedModelCheckpoint = fineTunedModelCheckpoint } + /** + * Alias for calling [Builder.fineTunedModelCheckpoint] with + * `fineTunedModelCheckpoint.orElse(null)`. + */ + fun fineTunedModelCheckpoint(fineTunedModelCheckpoint: Optional) = + fineTunedModelCheckpoint(fineTunedModelCheckpoint.getOrNull()) + /** Identifier for the last permission ID from the previous pagination request. */ fun after(after: String?) = apply { this.after = after } @@ -223,17 +224,10 @@ private constructor( * Returns an immutable instance of [PermissionRetrieveParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .fineTunedModelCheckpoint() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): PermissionRetrieveParams = PermissionRetrieveParams( - checkRequired("fineTunedModelCheckpoint", fineTunedModelCheckpoint), + fineTunedModelCheckpoint, after, limit, order, @@ -245,7 +239,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> fineTunedModelCheckpoint + 0 -> fineTunedModelCheckpoint ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJob.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJob.kt index 9c99d13c..61546aeb 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJob.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJob.kt @@ -26,6 +26,9 @@ import com.openai.core.checkRequired import com.openai.core.getOrThrow import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.finetuning.methods.DpoMethod +import com.openai.models.finetuning.methods.ReinforcementMethod +import com.openai.models.finetuning.methods.SupervisedMethod import java.util.Collections import java.util.Objects import java.util.Optional @@ -1387,7 +1390,10 @@ private constructor( * Number of examples in each batch. A larger batch size means that model parameters are * updated less frequently, but with lower variance. */ - fun batchSize(batchSize: BatchSize) = batchSize(JsonField.of(batchSize)) + fun batchSize(batchSize: BatchSize?) = batchSize(JsonField.ofNullable(batchSize)) + + /** Alias for calling [Builder.batchSize] with `batchSize.orElse(null)`. */ + fun batchSize(batchSize: Optional) = batchSize(batchSize.getOrNull()) /** * Sets [Builder.batchSize] to an arbitrary JSON value. @@ -1398,11 +1404,14 @@ private constructor( */ fun batchSize(batchSize: JsonField) = apply { this.batchSize = batchSize } - /** Alias for calling [batchSize] with `BatchSize.ofAuto()`. */ - fun batchSizeAuto() = batchSize(BatchSize.ofAuto()) + /** Alias for calling [batchSize] with `BatchSize.ofAuto(auto)`. */ + fun batchSize(auto: JsonValue) = batchSize(BatchSize.ofAuto(auto)) + + /** Alias for calling [batchSize] with `BatchSize.ofManual()`. */ + fun batchSizeManual() = batchSize(BatchSize.ofManual()) - /** Alias for calling [batchSize] with `BatchSize.ofManual(manual)`. */ - fun batchSize(manual: Long) = batchSize(BatchSize.ofManual(manual)) + /** Alias for calling [batchSize] with `BatchSize.ofInteger(integer)`. */ + fun batchSize(integer: Long) = batchSize(BatchSize.ofInteger(integer)) /** * Scaling factor for the learning rate. A smaller learning rate may be useful to avoid @@ -1532,21 +1541,28 @@ private constructor( class BatchSize private constructor( private val auto: JsonValue? = null, - private val manual: Long? = null, + private val manual: JsonValue? = null, + private val integer: Long? = null, private val _json: JsonValue? = null, ) { fun auto(): Optional = Optional.ofNullable(auto) - fun manual(): Optional = Optional.ofNullable(manual) + fun manual(): Optional = Optional.ofNullable(manual) + + fun integer(): Optional = Optional.ofNullable(integer) fun isAuto(): Boolean = auto != null fun isManual(): Boolean = manual != null + fun isInteger(): Boolean = integer != null + fun asAuto(): JsonValue = auto.getOrThrow("auto") - fun asManual(): Long = manual.getOrThrow("manual") + fun asManual(): JsonValue = manual.getOrThrow("manual") + + fun asInteger(): Long = integer.getOrThrow("integer") fun _json(): Optional = Optional.ofNullable(_json) @@ -1554,6 +1570,7 @@ private constructor( when { auto != null -> visitor.visitAuto(auto) manual != null -> visitor.visitManual(manual) + integer != null -> visitor.visitInteger(integer) else -> visitor.unknown(_json) } @@ -1566,17 +1583,19 @@ private constructor( accept( object : Visitor { - override fun visitAuto(auto: JsonValue) { - auto.let { + override fun visitAuto(auto: JsonValue) {} + + override fun visitManual(manual: JsonValue) { + manual.let { if (it != JsonValue.from("auto")) { throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" + "'manual' is invalid, received $it" ) } } } - override fun visitManual(manual: Long) {} + override fun visitInteger(integer: Long) {} } ) validated = true @@ -1600,10 +1619,12 @@ private constructor( internal fun validity(): Int = accept( object : Visitor { - override fun visitAuto(auto: JsonValue) = - auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + override fun visitAuto(auto: JsonValue) = 1 + + override fun visitManual(manual: JsonValue) = + manual.let { if (it == JsonValue.from("auto")) 1 else 0 } - override fun visitManual(manual: Long) = 1 + override fun visitInteger(integer: Long) = 1 override fun unknown(json: JsonValue?) = 0 } @@ -1614,24 +1635,27 @@ private constructor( return true } - return /* spotless:off */ other is BatchSize && auto == other.auto && manual == other.manual /* spotless:on */ + return /* spotless:off */ other is BatchSize && auto == other.auto && manual == other.manual && integer == other.integer /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual, integer) /* spotless:on */ override fun toString(): String = when { auto != null -> "BatchSize{auto=$auto}" manual != null -> "BatchSize{manual=$manual}" + integer != null -> "BatchSize{integer=$integer}" _json != null -> "BatchSize{_unknown=$_json}" else -> throw IllegalStateException("Invalid BatchSize") } companion object { - @JvmStatic fun ofAuto() = BatchSize(auto = JsonValue.from("auto")) + @JvmStatic fun ofAuto(auto: JsonValue) = BatchSize(auto = auto) + + @JvmStatic fun ofManual() = BatchSize(manual = JsonValue.from("auto")) - @JvmStatic fun ofManual(manual: Long) = BatchSize(manual = manual) + @JvmStatic fun ofInteger(integer: Long) = BatchSize(integer = integer) } /** @@ -1642,7 +1666,9 @@ private constructor( fun visitAuto(auto: JsonValue): T - fun visitManual(manual: Long): T + fun visitManual(manual: JsonValue): T + + fun visitInteger(integer: Long): T /** * Maps an unknown variant of [BatchSize] to a value of type [T]. @@ -1667,10 +1693,13 @@ private constructor( val bestMatches = sequenceOf( tryDeserialize(node, jacksonTypeRef()) - ?.let { BatchSize(auto = it, _json = json) } + ?.let { BatchSize(manual = it, _json = json) } ?.takeIf { it.isValid() }, tryDeserialize(node, jacksonTypeRef())?.let { - BatchSize(manual = it, _json = json) + BatchSize(integer = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + BatchSize(auto = it, _json = json) }, ) .filterNotNull() @@ -1678,7 +1707,7 @@ private constructor( .toList() return when (bestMatches.size) { // This can happen if what we're deserializing is completely incompatible - // with all the possible variants (e.g. deserializing from object). + // with all the possible variants. 0 -> BatchSize(_json = json) 1 -> bestMatches.single() // If there's more than one match with the highest validity, then use the @@ -1699,6 +1728,7 @@ private constructor( when { value.auto != null -> generator.writeObject(value.auto) value.manual != null -> generator.writeObject(value.manual) + value.integer != null -> generator.writeObject(value.integer) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid BatchSize") } @@ -2357,20 +2387,32 @@ private constructor( /** The method used for fine-tuning. */ class Method private constructor( - private val dpo: JsonField, - private val supervised: JsonField, private val type: JsonField, + private val dpo: JsonField, + private val reinforcement: JsonField, + private val supervised: JsonField, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("dpo") @ExcludeMissing dpo: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + @JsonProperty("dpo") @ExcludeMissing dpo: JsonField = JsonMissing.of(), + @JsonProperty("reinforcement") + @ExcludeMissing + reinforcement: JsonField = JsonMissing.of(), @JsonProperty("supervised") @ExcludeMissing - supervised: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), - ) : this(dpo, supervised, type, mutableMapOf()) + supervised: JsonField = JsonMissing.of(), + ) : this(type, dpo, reinforcement, supervised, mutableMapOf()) + + /** + * The type of method. Is either `supervised`, `dpo`, or `reinforcement`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun type(): Type = type.getRequired("type") /** * Configuration for the DPO fine-tuning method. @@ -2378,46 +2420,57 @@ private constructor( * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ - fun dpo(): Optional = dpo.getOptional("dpo") + fun dpo(): Optional = dpo.getOptional("dpo") /** - * Configuration for the supervised fine-tuning method. + * Configuration for the reinforcement fine-tuning method. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ - fun supervised(): Optional = supervised.getOptional("supervised") + fun reinforcement(): Optional = + reinforcement.getOptional("reinforcement") /** - * The type of method. Is either `supervised` or `dpo`. + * Configuration for the supervised fine-tuning method. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ - fun type(): Optional = type.getOptional("type") + fun supervised(): Optional = supervised.getOptional("supervised") + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type /** * Returns the raw JSON value of [dpo]. * * Unlike [dpo], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("dpo") @ExcludeMissing fun _dpo(): JsonField = dpo + @JsonProperty("dpo") @ExcludeMissing fun _dpo(): JsonField = dpo /** - * Returns the raw JSON value of [supervised]. + * Returns the raw JSON value of [reinforcement]. * - * Unlike [supervised], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [reinforcement], this method doesn't throw if the JSON field has an unexpected + * type. */ - @JsonProperty("supervised") + @JsonProperty("reinforcement") @ExcludeMissing - fun _supervised(): JsonField = supervised + fun _reinforcement(): JsonField = reinforcement /** - * Returns the raw JSON value of [type]. + * Returns the raw JSON value of [supervised]. * - * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [supervised], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + @JsonProperty("supervised") + @ExcludeMissing + fun _supervised(): JsonField = supervised @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -2433,63 +2486,87 @@ private constructor( companion object { - /** Returns a mutable builder for constructing an instance of [Method]. */ + /** + * Returns a mutable builder for constructing an instance of [Method]. + * + * The following fields are required: + * ```java + * .type() + * ``` + */ @JvmStatic fun builder() = Builder() } /** A builder for [Method]. */ class Builder internal constructor() { - private var dpo: JsonField = JsonMissing.of() - private var supervised: JsonField = JsonMissing.of() - private var type: JsonField = JsonMissing.of() + private var type: JsonField? = null + private var dpo: JsonField = JsonMissing.of() + private var reinforcement: JsonField = JsonMissing.of() + private var supervised: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(method: Method) = apply { + type = method.type dpo = method.dpo + reinforcement = method.reinforcement supervised = method.supervised - type = method.type additionalProperties = method.additionalProperties.toMutableMap() } - /** Configuration for the DPO fine-tuning method. */ - fun dpo(dpo: Dpo) = dpo(JsonField.of(dpo)) + /** The type of method. Is either `supervised`, `dpo`, or `reinforcement`. */ + fun type(type: Type) = type(JsonField.of(type)) /** - * Sets [Builder.dpo] to an arbitrary JSON value. + * Sets [Builder.type] to an arbitrary JSON value. * - * You should usually call [Builder.dpo] with a well-typed [Dpo] value instead. This + * You should usually call [Builder.type] with a well-typed [Type] value instead. This * method is primarily for setting the field to an undocumented or not yet supported * value. */ - fun dpo(dpo: JsonField) = apply { this.dpo = dpo } + fun type(type: JsonField) = apply { this.type = type } - /** Configuration for the supervised fine-tuning method. */ - fun supervised(supervised: Supervised) = supervised(JsonField.of(supervised)) + /** Configuration for the DPO fine-tuning method. */ + fun dpo(dpo: DpoMethod) = dpo(JsonField.of(dpo)) /** - * Sets [Builder.supervised] to an arbitrary JSON value. + * Sets [Builder.dpo] to an arbitrary JSON value. * - * You should usually call [Builder.supervised] with a well-typed [Supervised] value - * instead. This method is primarily for setting the field to an undocumented or not yet + * You should usually call [Builder.dpo] with a well-typed [DpoMethod] value instead. + * This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun supervised(supervised: JsonField) = apply { - this.supervised = supervised + fun dpo(dpo: JsonField) = apply { this.dpo = dpo } + + /** Configuration for the reinforcement fine-tuning method. */ + fun reinforcement(reinforcement: ReinforcementMethod) = + reinforcement(JsonField.of(reinforcement)) + + /** + * Sets [Builder.reinforcement] to an arbitrary JSON value. + * + * You should usually call [Builder.reinforcement] with a well-typed + * [ReinforcementMethod] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun reinforcement(reinforcement: JsonField) = apply { + this.reinforcement = reinforcement } - /** The type of method. Is either `supervised` or `dpo`. */ - fun type(type: Type) = type(JsonField.of(type)) + /** Configuration for the supervised fine-tuning method. */ + fun supervised(supervised: SupervisedMethod) = supervised(JsonField.of(supervised)) /** - * Sets [Builder.type] to an arbitrary JSON value. + * Sets [Builder.supervised] to an arbitrary JSON value. * - * You should usually call [Builder.type] with a well-typed [Type] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported - * value. + * You should usually call [Builder.supervised] with a well-typed [SupervisedMethod] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. */ - fun type(type: JsonField) = apply { this.type = type } + fun supervised(supervised: JsonField) = apply { + this.supervised = supervised + } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -2514,8 +2591,22 @@ private constructor( * Returns an immutable instance of [Method]. * * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .type() + * ``` + * + * @throws IllegalStateException if any required field is unset. */ - fun build(): Method = Method(dpo, supervised, type, additionalProperties.toMutableMap()) + fun build(): Method = + Method( + checkRequired("type", type), + dpo, + reinforcement, + supervised, + additionalProperties.toMutableMap(), + ) } private var validated: Boolean = false @@ -2525,9 +2616,10 @@ private constructor( return@apply } + type().validate() dpo().ifPresent { it.validate() } + reinforcement().ifPresent { it.validate() } supervised().ifPresent { it.validate() } - type().ifPresent { it.validate() } validated = true } @@ -2547,2263 +2639,31 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (dpo.asKnown().getOrNull()?.validity() ?: 0) + - (supervised.asKnown().getOrNull()?.validity() ?: 0) + - (type.asKnown().getOrNull()?.validity() ?: 0) - - /** Configuration for the DPO fine-tuning method. */ - class Dpo - private constructor( - private val hyperparameters: JsonField, - private val additionalProperties: MutableMap, - ) { + (type.asKnown().getOrNull()?.validity() ?: 0) + + (dpo.asKnown().getOrNull()?.validity() ?: 0) + + (reinforcement.asKnown().getOrNull()?.validity() ?: 0) + + (supervised.asKnown().getOrNull()?.validity() ?: 0) - @JsonCreator - private constructor( - @JsonProperty("hyperparameters") - @ExcludeMissing - hyperparameters: JsonField = JsonMissing.of() - ) : this(hyperparameters, mutableMapOf()) - - /** - * The hyperparameters used for the fine-tuning job. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun hyperparameters(): Optional = - hyperparameters.getOptional("hyperparameters") + /** The type of method. Is either `supervised`, `dpo`, or `reinforcement`. */ + class Type @JsonCreator private constructor(private val value: JsonField) : Enum { /** - * Returns the raw JSON value of [hyperparameters]. + * Returns this class instance's raw value. * - * Unlike [hyperparameters], this method doesn't throw if the JSON field has an - * unexpected type. + * 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. */ - @JsonProperty("hyperparameters") - @ExcludeMissing - fun _hyperparameters(): JsonField = hyperparameters - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value companion object { - /** Returns a mutable builder for constructing an instance of [Dpo]. */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [Dpo]. */ - class Builder internal constructor() { - - private var hyperparameters: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(dpo: Dpo) = apply { - hyperparameters = dpo.hyperparameters - additionalProperties = dpo.additionalProperties.toMutableMap() - } - - /** The hyperparameters used for the fine-tuning job. */ - fun hyperparameters(hyperparameters: Hyperparameters) = - hyperparameters(JsonField.of(hyperparameters)) - - /** - * Sets [Builder.hyperparameters] to an arbitrary JSON value. - * - * You should usually call [Builder.hyperparameters] with a well-typed - * [Hyperparameters] value instead. This method is primarily for setting the field - * to an undocumented or not yet supported value. - */ - fun hyperparameters(hyperparameters: JsonField) = apply { - this.hyperparameters = hyperparameters - } - - 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) - } - - /** - * Returns an immutable instance of [Dpo]. - * - * Further updates to this [Builder] will not mutate the returned instance. - */ - fun build(): Dpo = Dpo(hyperparameters, additionalProperties.toMutableMap()) - } - - private var validated: Boolean = false - - fun validate(): Dpo = apply { - if (validated) { - return@apply - } - - hyperparameters().ifPresent { it.validate() } - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = (hyperparameters.asKnown().getOrNull()?.validity() ?: 0) - - /** The hyperparameters used for the fine-tuning job. */ - class Hyperparameters - private constructor( - private val batchSize: JsonField, - private val beta: JsonField, - private val learningRateMultiplier: JsonField, - private val nEpochs: JsonField, - private val additionalProperties: MutableMap, - ) { - - @JsonCreator - private constructor( - @JsonProperty("batch_size") - @ExcludeMissing - batchSize: JsonField = JsonMissing.of(), - @JsonProperty("beta") @ExcludeMissing beta: JsonField = JsonMissing.of(), - @JsonProperty("learning_rate_multiplier") - @ExcludeMissing - learningRateMultiplier: JsonField = JsonMissing.of(), - @JsonProperty("n_epochs") - @ExcludeMissing - nEpochs: JsonField = JsonMissing.of(), - ) : this(batchSize, beta, learningRateMultiplier, nEpochs, mutableMapOf()) - - /** - * Number of examples in each batch. A larger batch size means that model parameters - * are updated less frequently, but with lower variance. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun batchSize(): Optional = batchSize.getOptional("batch_size") - - /** - * The beta value for the DPO method. A higher beta value will increase the weight - * of the penalty between the policy and reference model. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun beta(): Optional = beta.getOptional("beta") - - /** - * Scaling factor for the learning rate. A smaller learning rate may be useful to - * avoid overfitting. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun learningRateMultiplier(): Optional = - learningRateMultiplier.getOptional("learning_rate_multiplier") - - /** - * The number of epochs to train the model for. An epoch refers to one full cycle - * through the training dataset. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun nEpochs(): Optional = nEpochs.getOptional("n_epochs") - - /** - * Returns the raw JSON value of [batchSize]. - * - * Unlike [batchSize], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("batch_size") - @ExcludeMissing - fun _batchSize(): JsonField = batchSize - - /** - * Returns the raw JSON value of [beta]. - * - * Unlike [beta], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("beta") @ExcludeMissing fun _beta(): JsonField = beta - - /** - * Returns the raw JSON value of [learningRateMultiplier]. - * - * Unlike [learningRateMultiplier], this method doesn't throw if the JSON field has - * an unexpected type. - */ - @JsonProperty("learning_rate_multiplier") - @ExcludeMissing - fun _learningRateMultiplier(): JsonField = - learningRateMultiplier - - /** - * Returns the raw JSON value of [nEpochs]. - * - * Unlike [nEpochs], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("n_epochs") - @ExcludeMissing - fun _nEpochs(): JsonField = nEpochs - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { - - /** - * Returns a mutable builder for constructing an instance of [Hyperparameters]. - */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [Hyperparameters]. */ - class Builder internal constructor() { - - private var batchSize: JsonField = JsonMissing.of() - private var beta: JsonField = JsonMissing.of() - private var learningRateMultiplier: JsonField = - JsonMissing.of() - private var nEpochs: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(hyperparameters: Hyperparameters) = apply { - batchSize = hyperparameters.batchSize - beta = hyperparameters.beta - learningRateMultiplier = hyperparameters.learningRateMultiplier - nEpochs = hyperparameters.nEpochs - additionalProperties = hyperparameters.additionalProperties.toMutableMap() - } - - /** - * Number of examples in each batch. A larger batch size means that model - * parameters are updated less frequently, but with lower variance. - */ - fun batchSize(batchSize: BatchSize) = batchSize(JsonField.of(batchSize)) - - /** - * Sets [Builder.batchSize] to an arbitrary JSON value. - * - * You should usually call [Builder.batchSize] with a well-typed [BatchSize] - * value instead. This method is primarily for setting the field to an - * undocumented or not yet supported value. - */ - fun batchSize(batchSize: JsonField) = apply { - this.batchSize = batchSize - } - - /** Alias for calling [batchSize] with `BatchSize.ofAuto()`. */ - fun batchSizeAuto() = batchSize(BatchSize.ofAuto()) - - /** Alias for calling [batchSize] with `BatchSize.ofManual(manual)`. */ - fun batchSize(manual: Long) = batchSize(BatchSize.ofManual(manual)) - - /** - * The beta value for the DPO method. A higher beta value will increase the - * weight of the penalty between the policy and reference model. - */ - fun beta(beta: Beta) = beta(JsonField.of(beta)) - - /** - * Sets [Builder.beta] to an arbitrary JSON value. - * - * You should usually call [Builder.beta] with a well-typed [Beta] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun beta(beta: JsonField) = apply { this.beta = beta } - - /** Alias for calling [beta] with `Beta.ofAuto()`. */ - fun betaAuto() = beta(Beta.ofAuto()) - - /** Alias for calling [beta] with `Beta.ofManual(manual)`. */ - fun beta(manual: Double) = beta(Beta.ofManual(manual)) - - /** - * Scaling factor for the learning rate. A smaller learning rate may be useful - * to avoid overfitting. - */ - fun learningRateMultiplier(learningRateMultiplier: LearningRateMultiplier) = - learningRateMultiplier(JsonField.of(learningRateMultiplier)) - - /** - * Sets [Builder.learningRateMultiplier] to an arbitrary JSON value. - * - * You should usually call [Builder.learningRateMultiplier] with a well-typed - * [LearningRateMultiplier] value instead. This method is primarily for setting - * the field to an undocumented or not yet supported value. - */ - fun learningRateMultiplier( - learningRateMultiplier: JsonField - ) = apply { this.learningRateMultiplier = learningRateMultiplier } - - /** - * Alias for calling [learningRateMultiplier] with - * `LearningRateMultiplier.ofAuto()`. - */ - fun learningRateMultiplierAuto() = - learningRateMultiplier(LearningRateMultiplier.ofAuto()) - - /** - * Alias for calling [learningRateMultiplier] with - * `LearningRateMultiplier.ofManual(manual)`. - */ - fun learningRateMultiplier(manual: Double) = - learningRateMultiplier(LearningRateMultiplier.ofManual(manual)) - - /** - * The number of epochs to train the model for. An epoch refers to one full - * cycle through the training dataset. - */ - fun nEpochs(nEpochs: NEpochs) = nEpochs(JsonField.of(nEpochs)) - - /** - * Sets [Builder.nEpochs] to an arbitrary JSON value. - * - * You should usually call [Builder.nEpochs] with a well-typed [NEpochs] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun nEpochs(nEpochs: JsonField) = apply { this.nEpochs = nEpochs } - - /** Alias for calling [nEpochs] with `NEpochs.ofAuto()`. */ - fun nEpochsAuto() = nEpochs(NEpochs.ofAuto()) - - /** Alias for calling [nEpochs] with `NEpochs.ofManual(manual)`. */ - fun nEpochs(manual: Long) = nEpochs(NEpochs.ofManual(manual)) - - 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) - } - - /** - * Returns an immutable instance of [Hyperparameters]. - * - * Further updates to this [Builder] will not mutate the returned instance. - */ - fun build(): Hyperparameters = - Hyperparameters( - batchSize, - beta, - learningRateMultiplier, - nEpochs, - additionalProperties.toMutableMap(), - ) - } - - private var validated: Boolean = false - - fun validate(): Hyperparameters = apply { - if (validated) { - return@apply - } - - batchSize().ifPresent { it.validate() } - beta().ifPresent { it.validate() } - learningRateMultiplier().ifPresent { it.validate() } - nEpochs().ifPresent { it.validate() } - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (batchSize.asKnown().getOrNull()?.validity() ?: 0) + - (beta.asKnown().getOrNull()?.validity() ?: 0) + - (learningRateMultiplier.asKnown().getOrNull()?.validity() ?: 0) + - (nEpochs.asKnown().getOrNull()?.validity() ?: 0) - - /** - * Number of examples in each batch. A larger batch size means that model parameters - * are updated less frequently, but with lower variance. - */ - @JsonDeserialize(using = BatchSize.Deserializer::class) - @JsonSerialize(using = BatchSize.Serializer::class) - class BatchSize - private constructor( - private val auto: JsonValue? = null, - private val manual: Long? = null, - private val _json: JsonValue? = null, - ) { - - fun auto(): Optional = Optional.ofNullable(auto) - - fun manual(): Optional = Optional.ofNullable(manual) - - fun isAuto(): Boolean = auto != null - - fun isManual(): Boolean = manual != null - - fun asAuto(): JsonValue = auto.getOrThrow("auto") - - fun asManual(): Long = manual.getOrThrow("manual") - - fun _json(): Optional = Optional.ofNullable(_json) - - fun accept(visitor: Visitor): T = - when { - auto != null -> visitor.visitAuto(auto) - manual != null -> visitor.visitManual(manual) - else -> visitor.unknown(_json) - } - - private var validated: Boolean = false - - fun validate(): BatchSize = apply { - if (validated) { - return@apply - } + @JvmField val SUPERVISED = of("supervised") - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) { - auto.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } + @JvmField val DPO = of("dpo") - override fun visitManual(manual: Long) {} - } - ) - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) = - auto.let { if (it == JsonValue.from("auto")) 1 else 0 } - - override fun visitManual(manual: Long) = 1 - - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is BatchSize && auto == other.auto && manual == other.manual /* spotless:on */ - } - - override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual) /* spotless:on */ - - override fun toString(): String = - when { - auto != null -> "BatchSize{auto=$auto}" - manual != null -> "BatchSize{manual=$manual}" - _json != null -> "BatchSize{_unknown=$_json}" - else -> throw IllegalStateException("Invalid BatchSize") - } - - companion object { - - @JvmStatic fun ofAuto() = BatchSize(auto = JsonValue.from("auto")) - - @JvmStatic fun ofManual(manual: Long) = BatchSize(manual = manual) - } - - /** - * An interface that defines how to map each variant of [BatchSize] to a value - * of type [T]. - */ - interface Visitor { - - fun visitAuto(auto: JsonValue): T - - fun visitManual(manual: Long): T - - /** - * Maps an unknown variant of [BatchSize] to a value of type [T]. - * - * An instance of [BatchSize] 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 BatchSize: $json") - } - } - - internal class Deserializer : BaseDeserializer(BatchSize::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): BatchSize { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { BatchSize(auto = it, _json = json) } - ?.takeIf { it.isValid() }, - tryDeserialize(node, jacksonTypeRef())?.let { - BatchSize(manual = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from object). - 0 -> BatchSize(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } - } - - internal class Serializer : BaseSerializer(BatchSize::class) { - - override fun serialize( - value: BatchSize, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.auto != null -> generator.writeObject(value.auto) - value.manual != null -> generator.writeObject(value.manual) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid BatchSize") - } - } - } - } - - /** - * The beta value for the DPO method. A higher beta value will increase the weight - * of the penalty between the policy and reference model. - */ - @JsonDeserialize(using = Beta.Deserializer::class) - @JsonSerialize(using = Beta.Serializer::class) - class Beta - private constructor( - private val auto: JsonValue? = null, - private val manual: Double? = null, - private val _json: JsonValue? = null, - ) { - - fun auto(): Optional = Optional.ofNullable(auto) - - fun manual(): Optional = Optional.ofNullable(manual) - - fun isAuto(): Boolean = auto != null - - fun isManual(): Boolean = manual != null - - fun asAuto(): JsonValue = auto.getOrThrow("auto") - - fun asManual(): Double = manual.getOrThrow("manual") - - fun _json(): Optional = Optional.ofNullable(_json) - - fun accept(visitor: Visitor): T = - when { - auto != null -> visitor.visitAuto(auto) - manual != null -> visitor.visitManual(manual) - else -> visitor.unknown(_json) - } - - private var validated: Boolean = false - - fun validate(): Beta = apply { - if (validated) { - return@apply - } - - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) { - auto.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - - override fun visitManual(manual: Double) {} - } - ) - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) = - auto.let { if (it == JsonValue.from("auto")) 1 else 0 } - - override fun visitManual(manual: Double) = 1 - - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is Beta && auto == other.auto && manual == other.manual /* spotless:on */ - } - - override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual) /* spotless:on */ - - override fun toString(): String = - when { - auto != null -> "Beta{auto=$auto}" - manual != null -> "Beta{manual=$manual}" - _json != null -> "Beta{_unknown=$_json}" - else -> throw IllegalStateException("Invalid Beta") - } - - companion object { - - @JvmStatic fun ofAuto() = Beta(auto = JsonValue.from("auto")) - - @JvmStatic fun ofManual(manual: Double) = Beta(manual = manual) - } - - /** - * An interface that defines how to map each variant of [Beta] to a value of - * type [T]. - */ - interface Visitor { - - fun visitAuto(auto: JsonValue): T - - fun visitManual(manual: Double): T - - /** - * Maps an unknown variant of [Beta] to a value of type [T]. - * - * An instance of [Beta] 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 Beta: $json") - } - } - - internal class Deserializer : BaseDeserializer(Beta::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): Beta { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { Beta(auto = it, _json = json) } - ?.takeIf { it.isValid() }, - tryDeserialize(node, jacksonTypeRef())?.let { - Beta(manual = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from object). - 0 -> Beta(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } - } - - internal class Serializer : BaseSerializer(Beta::class) { - - override fun serialize( - value: Beta, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.auto != null -> generator.writeObject(value.auto) - value.manual != null -> generator.writeObject(value.manual) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid Beta") - } - } - } - } - - /** - * Scaling factor for the learning rate. A smaller learning rate may be useful to - * avoid overfitting. - */ - @JsonDeserialize(using = LearningRateMultiplier.Deserializer::class) - @JsonSerialize(using = LearningRateMultiplier.Serializer::class) - class LearningRateMultiplier - private constructor( - private val auto: JsonValue? = null, - private val manual: Double? = null, - private val _json: JsonValue? = null, - ) { - - fun auto(): Optional = Optional.ofNullable(auto) - - fun manual(): Optional = Optional.ofNullable(manual) - - fun isAuto(): Boolean = auto != null - - fun isManual(): Boolean = manual != null - - fun asAuto(): JsonValue = auto.getOrThrow("auto") - - fun asManual(): Double = manual.getOrThrow("manual") - - fun _json(): Optional = Optional.ofNullable(_json) - - fun accept(visitor: Visitor): T = - when { - auto != null -> visitor.visitAuto(auto) - manual != null -> visitor.visitManual(manual) - else -> visitor.unknown(_json) - } - - private var validated: Boolean = false - - fun validate(): LearningRateMultiplier = apply { - if (validated) { - return@apply - } - - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) { - auto.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - - override fun visitManual(manual: Double) {} - } - ) - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) = - auto.let { if (it == JsonValue.from("auto")) 1 else 0 } - - override fun visitManual(manual: Double) = 1 - - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is LearningRateMultiplier && auto == other.auto && manual == other.manual /* spotless:on */ - } - - override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual) /* spotless:on */ - - override fun toString(): String = - when { - auto != null -> "LearningRateMultiplier{auto=$auto}" - manual != null -> "LearningRateMultiplier{manual=$manual}" - _json != null -> "LearningRateMultiplier{_unknown=$_json}" - else -> throw IllegalStateException("Invalid LearningRateMultiplier") - } - - companion object { - - @JvmStatic - fun ofAuto() = LearningRateMultiplier(auto = JsonValue.from("auto")) - - @JvmStatic - fun ofManual(manual: Double) = LearningRateMultiplier(manual = manual) - } - - /** - * An interface that defines how to map each variant of [LearningRateMultiplier] - * to a value of type [T]. - */ - interface Visitor { - - fun visitAuto(auto: JsonValue): T - - fun visitManual(manual: Double): T - - /** - * Maps an unknown variant of [LearningRateMultiplier] to a value of type - * [T]. - * - * An instance of [LearningRateMultiplier] 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 LearningRateMultiplier: $json" - ) - } - } - - internal class Deserializer : - BaseDeserializer(LearningRateMultiplier::class) { - - override fun ObjectCodec.deserialize( - node: JsonNode - ): LearningRateMultiplier { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { - LearningRateMultiplier(auto = it, _json = json) - } - ?.takeIf { it.isValid() }, - tryDeserialize(node, jacksonTypeRef())?.let { - LearningRateMultiplier(manual = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from object). - 0 -> LearningRateMultiplier(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } - } - - internal class Serializer : - BaseSerializer(LearningRateMultiplier::class) { - - override fun serialize( - value: LearningRateMultiplier, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.auto != null -> generator.writeObject(value.auto) - value.manual != null -> generator.writeObject(value.manual) - value._json != null -> generator.writeObject(value._json) - else -> - throw IllegalStateException("Invalid LearningRateMultiplier") - } - } - } - } - - /** - * The number of epochs to train the model for. An epoch refers to one full cycle - * through the training dataset. - */ - @JsonDeserialize(using = NEpochs.Deserializer::class) - @JsonSerialize(using = NEpochs.Serializer::class) - class NEpochs - private constructor( - private val auto: JsonValue? = null, - private val manual: Long? = null, - private val _json: JsonValue? = null, - ) { - - fun auto(): Optional = Optional.ofNullable(auto) - - fun manual(): Optional = Optional.ofNullable(manual) - - fun isAuto(): Boolean = auto != null - - fun isManual(): Boolean = manual != null - - fun asAuto(): JsonValue = auto.getOrThrow("auto") - - fun asManual(): Long = manual.getOrThrow("manual") - - fun _json(): Optional = Optional.ofNullable(_json) - - fun accept(visitor: Visitor): T = - when { - auto != null -> visitor.visitAuto(auto) - manual != null -> visitor.visitManual(manual) - else -> visitor.unknown(_json) - } - - private var validated: Boolean = false - - fun validate(): NEpochs = apply { - if (validated) { - return@apply - } - - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) { - auto.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - - override fun visitManual(manual: Long) {} - } - ) - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) = - auto.let { if (it == JsonValue.from("auto")) 1 else 0 } - - override fun visitManual(manual: Long) = 1 - - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is NEpochs && auto == other.auto && manual == other.manual /* spotless:on */ - } - - override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual) /* spotless:on */ - - override fun toString(): String = - when { - auto != null -> "NEpochs{auto=$auto}" - manual != null -> "NEpochs{manual=$manual}" - _json != null -> "NEpochs{_unknown=$_json}" - else -> throw IllegalStateException("Invalid NEpochs") - } - - companion object { - - @JvmStatic fun ofAuto() = NEpochs(auto = JsonValue.from("auto")) - - @JvmStatic fun ofManual(manual: Long) = NEpochs(manual = manual) - } - - /** - * An interface that defines how to map each variant of [NEpochs] to a value of - * type [T]. - */ - interface Visitor { - - fun visitAuto(auto: JsonValue): T - - fun visitManual(manual: Long): T - - /** - * Maps an unknown variant of [NEpochs] to a value of type [T]. - * - * An instance of [NEpochs] 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 NEpochs: $json") - } - } - - internal class Deserializer : BaseDeserializer(NEpochs::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): NEpochs { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { NEpochs(auto = it, _json = json) } - ?.takeIf { it.isValid() }, - tryDeserialize(node, jacksonTypeRef())?.let { - NEpochs(manual = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from object). - 0 -> NEpochs(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } - } - - internal class Serializer : BaseSerializer(NEpochs::class) { - - override fun serialize( - value: NEpochs, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.auto != null -> generator.writeObject(value.auto) - value.manual != null -> generator.writeObject(value.manual) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid NEpochs") - } - } - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is Hyperparameters && batchSize == other.batchSize && beta == other.beta && learningRateMultiplier == other.learningRateMultiplier && nEpochs == other.nEpochs && additionalProperties == other.additionalProperties /* spotless:on */ - } - - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(batchSize, beta, learningRateMultiplier, nEpochs, additionalProperties) } - /* spotless:on */ - - override fun hashCode(): Int = hashCode - - override fun toString() = - "Hyperparameters{batchSize=$batchSize, beta=$beta, learningRateMultiplier=$learningRateMultiplier, nEpochs=$nEpochs, additionalProperties=$additionalProperties}" - } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is Dpo && hyperparameters == other.hyperparameters && additionalProperties == other.additionalProperties /* spotless:on */ - } - - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(hyperparameters, additionalProperties) } - /* spotless:on */ - - override fun hashCode(): Int = hashCode - - override fun toString() = - "Dpo{hyperparameters=$hyperparameters, additionalProperties=$additionalProperties}" - } - - /** Configuration for the supervised fine-tuning method. */ - class Supervised - private constructor( - private val hyperparameters: JsonField, - private val additionalProperties: MutableMap, - ) { - - @JsonCreator - private constructor( - @JsonProperty("hyperparameters") - @ExcludeMissing - hyperparameters: JsonField = JsonMissing.of() - ) : this(hyperparameters, mutableMapOf()) - - /** - * The hyperparameters used for the fine-tuning job. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun hyperparameters(): Optional = - hyperparameters.getOptional("hyperparameters") - - /** - * Returns the raw JSON value of [hyperparameters]. - * - * Unlike [hyperparameters], this method doesn't throw if the JSON field has an - * unexpected type. - */ - @JsonProperty("hyperparameters") - @ExcludeMissing - fun _hyperparameters(): JsonField = hyperparameters - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { - - /** Returns a mutable builder for constructing an instance of [Supervised]. */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [Supervised]. */ - class Builder internal constructor() { - - private var hyperparameters: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(supervised: Supervised) = apply { - hyperparameters = supervised.hyperparameters - additionalProperties = supervised.additionalProperties.toMutableMap() - } - - /** The hyperparameters used for the fine-tuning job. */ - fun hyperparameters(hyperparameters: Hyperparameters) = - hyperparameters(JsonField.of(hyperparameters)) - - /** - * Sets [Builder.hyperparameters] to an arbitrary JSON value. - * - * You should usually call [Builder.hyperparameters] with a well-typed - * [Hyperparameters] value instead. This method is primarily for setting the field - * to an undocumented or not yet supported value. - */ - fun hyperparameters(hyperparameters: JsonField) = apply { - this.hyperparameters = hyperparameters - } - - 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) - } - - /** - * Returns an immutable instance of [Supervised]. - * - * Further updates to this [Builder] will not mutate the returned instance. - */ - fun build(): Supervised = - Supervised(hyperparameters, additionalProperties.toMutableMap()) - } - - private var validated: Boolean = false - - fun validate(): Supervised = apply { - if (validated) { - return@apply - } - - hyperparameters().ifPresent { it.validate() } - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = (hyperparameters.asKnown().getOrNull()?.validity() ?: 0) - - /** The hyperparameters used for the fine-tuning job. */ - class Hyperparameters - private constructor( - private val batchSize: JsonField, - private val learningRateMultiplier: JsonField, - private val nEpochs: JsonField, - private val additionalProperties: MutableMap, - ) { - - @JsonCreator - private constructor( - @JsonProperty("batch_size") - @ExcludeMissing - batchSize: JsonField = JsonMissing.of(), - @JsonProperty("learning_rate_multiplier") - @ExcludeMissing - learningRateMultiplier: JsonField = JsonMissing.of(), - @JsonProperty("n_epochs") - @ExcludeMissing - nEpochs: JsonField = JsonMissing.of(), - ) : this(batchSize, learningRateMultiplier, nEpochs, mutableMapOf()) - - /** - * Number of examples in each batch. A larger batch size means that model parameters - * are updated less frequently, but with lower variance. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun batchSize(): Optional = batchSize.getOptional("batch_size") - - /** - * Scaling factor for the learning rate. A smaller learning rate may be useful to - * avoid overfitting. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun learningRateMultiplier(): Optional = - learningRateMultiplier.getOptional("learning_rate_multiplier") - - /** - * The number of epochs to train the model for. An epoch refers to one full cycle - * through the training dataset. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun nEpochs(): Optional = nEpochs.getOptional("n_epochs") - - /** - * Returns the raw JSON value of [batchSize]. - * - * Unlike [batchSize], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("batch_size") - @ExcludeMissing - fun _batchSize(): JsonField = batchSize - - /** - * Returns the raw JSON value of [learningRateMultiplier]. - * - * Unlike [learningRateMultiplier], this method doesn't throw if the JSON field has - * an unexpected type. - */ - @JsonProperty("learning_rate_multiplier") - @ExcludeMissing - fun _learningRateMultiplier(): JsonField = - learningRateMultiplier - - /** - * Returns the raw JSON value of [nEpochs]. - * - * Unlike [nEpochs], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("n_epochs") - @ExcludeMissing - fun _nEpochs(): JsonField = nEpochs - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { - - /** - * Returns a mutable builder for constructing an instance of [Hyperparameters]. - */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [Hyperparameters]. */ - class Builder internal constructor() { - - private var batchSize: JsonField = JsonMissing.of() - private var learningRateMultiplier: JsonField = - JsonMissing.of() - private var nEpochs: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(hyperparameters: Hyperparameters) = apply { - batchSize = hyperparameters.batchSize - learningRateMultiplier = hyperparameters.learningRateMultiplier - nEpochs = hyperparameters.nEpochs - additionalProperties = hyperparameters.additionalProperties.toMutableMap() - } - - /** - * Number of examples in each batch. A larger batch size means that model - * parameters are updated less frequently, but with lower variance. - */ - fun batchSize(batchSize: BatchSize) = batchSize(JsonField.of(batchSize)) - - /** - * Sets [Builder.batchSize] to an arbitrary JSON value. - * - * You should usually call [Builder.batchSize] with a well-typed [BatchSize] - * value instead. This method is primarily for setting the field to an - * undocumented or not yet supported value. - */ - fun batchSize(batchSize: JsonField) = apply { - this.batchSize = batchSize - } - - /** Alias for calling [batchSize] with `BatchSize.ofAuto()`. */ - fun batchSizeAuto() = batchSize(BatchSize.ofAuto()) - - /** Alias for calling [batchSize] with `BatchSize.ofManual(manual)`. */ - fun batchSize(manual: Long) = batchSize(BatchSize.ofManual(manual)) - - /** - * Scaling factor for the learning rate. A smaller learning rate may be useful - * to avoid overfitting. - */ - fun learningRateMultiplier(learningRateMultiplier: LearningRateMultiplier) = - learningRateMultiplier(JsonField.of(learningRateMultiplier)) - - /** - * Sets [Builder.learningRateMultiplier] to an arbitrary JSON value. - * - * You should usually call [Builder.learningRateMultiplier] with a well-typed - * [LearningRateMultiplier] value instead. This method is primarily for setting - * the field to an undocumented or not yet supported value. - */ - fun learningRateMultiplier( - learningRateMultiplier: JsonField - ) = apply { this.learningRateMultiplier = learningRateMultiplier } - - /** - * Alias for calling [learningRateMultiplier] with - * `LearningRateMultiplier.ofAuto()`. - */ - fun learningRateMultiplierAuto() = - learningRateMultiplier(LearningRateMultiplier.ofAuto()) - - /** - * Alias for calling [learningRateMultiplier] with - * `LearningRateMultiplier.ofManual(manual)`. - */ - fun learningRateMultiplier(manual: Double) = - learningRateMultiplier(LearningRateMultiplier.ofManual(manual)) - - /** - * The number of epochs to train the model for. An epoch refers to one full - * cycle through the training dataset. - */ - fun nEpochs(nEpochs: NEpochs) = nEpochs(JsonField.of(nEpochs)) - - /** - * Sets [Builder.nEpochs] to an arbitrary JSON value. - * - * You should usually call [Builder.nEpochs] with a well-typed [NEpochs] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun nEpochs(nEpochs: JsonField) = apply { this.nEpochs = nEpochs } - - /** Alias for calling [nEpochs] with `NEpochs.ofAuto()`. */ - fun nEpochsAuto() = nEpochs(NEpochs.ofAuto()) - - /** Alias for calling [nEpochs] with `NEpochs.ofManual(manual)`. */ - fun nEpochs(manual: Long) = nEpochs(NEpochs.ofManual(manual)) - - 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) - } - - /** - * Returns an immutable instance of [Hyperparameters]. - * - * Further updates to this [Builder] will not mutate the returned instance. - */ - fun build(): Hyperparameters = - Hyperparameters( - batchSize, - learningRateMultiplier, - nEpochs, - additionalProperties.toMutableMap(), - ) - } - - private var validated: Boolean = false - - fun validate(): Hyperparameters = apply { - if (validated) { - return@apply - } - - batchSize().ifPresent { it.validate() } - learningRateMultiplier().ifPresent { it.validate() } - nEpochs().ifPresent { it.validate() } - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (batchSize.asKnown().getOrNull()?.validity() ?: 0) + - (learningRateMultiplier.asKnown().getOrNull()?.validity() ?: 0) + - (nEpochs.asKnown().getOrNull()?.validity() ?: 0) - - /** - * Number of examples in each batch. A larger batch size means that model parameters - * are updated less frequently, but with lower variance. - */ - @JsonDeserialize(using = BatchSize.Deserializer::class) - @JsonSerialize(using = BatchSize.Serializer::class) - class BatchSize - private constructor( - private val auto: JsonValue? = null, - private val manual: Long? = null, - private val _json: JsonValue? = null, - ) { - - fun auto(): Optional = Optional.ofNullable(auto) - - fun manual(): Optional = Optional.ofNullable(manual) - - fun isAuto(): Boolean = auto != null - - fun isManual(): Boolean = manual != null - - fun asAuto(): JsonValue = auto.getOrThrow("auto") - - fun asManual(): Long = manual.getOrThrow("manual") - - fun _json(): Optional = Optional.ofNullable(_json) - - fun accept(visitor: Visitor): T = - when { - auto != null -> visitor.visitAuto(auto) - manual != null -> visitor.visitManual(manual) - else -> visitor.unknown(_json) - } - - private var validated: Boolean = false - - fun validate(): BatchSize = apply { - if (validated) { - return@apply - } - - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) { - auto.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - - override fun visitManual(manual: Long) {} - } - ) - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) = - auto.let { if (it == JsonValue.from("auto")) 1 else 0 } - - override fun visitManual(manual: Long) = 1 - - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is BatchSize && auto == other.auto && manual == other.manual /* spotless:on */ - } - - override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual) /* spotless:on */ - - override fun toString(): String = - when { - auto != null -> "BatchSize{auto=$auto}" - manual != null -> "BatchSize{manual=$manual}" - _json != null -> "BatchSize{_unknown=$_json}" - else -> throw IllegalStateException("Invalid BatchSize") - } - - companion object { - - @JvmStatic fun ofAuto() = BatchSize(auto = JsonValue.from("auto")) - - @JvmStatic fun ofManual(manual: Long) = BatchSize(manual = manual) - } - - /** - * An interface that defines how to map each variant of [BatchSize] to a value - * of type [T]. - */ - interface Visitor { - - fun visitAuto(auto: JsonValue): T - - fun visitManual(manual: Long): T - - /** - * Maps an unknown variant of [BatchSize] to a value of type [T]. - * - * An instance of [BatchSize] 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 BatchSize: $json") - } - } - - internal class Deserializer : BaseDeserializer(BatchSize::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): BatchSize { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { BatchSize(auto = it, _json = json) } - ?.takeIf { it.isValid() }, - tryDeserialize(node, jacksonTypeRef())?.let { - BatchSize(manual = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from object). - 0 -> BatchSize(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } - } - - internal class Serializer : BaseSerializer(BatchSize::class) { - - override fun serialize( - value: BatchSize, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.auto != null -> generator.writeObject(value.auto) - value.manual != null -> generator.writeObject(value.manual) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid BatchSize") - } - } - } - } - - /** - * Scaling factor for the learning rate. A smaller learning rate may be useful to - * avoid overfitting. - */ - @JsonDeserialize(using = LearningRateMultiplier.Deserializer::class) - @JsonSerialize(using = LearningRateMultiplier.Serializer::class) - class LearningRateMultiplier - private constructor( - private val auto: JsonValue? = null, - private val manual: Double? = null, - private val _json: JsonValue? = null, - ) { - - fun auto(): Optional = Optional.ofNullable(auto) - - fun manual(): Optional = Optional.ofNullable(manual) - - fun isAuto(): Boolean = auto != null - - fun isManual(): Boolean = manual != null - - fun asAuto(): JsonValue = auto.getOrThrow("auto") - - fun asManual(): Double = manual.getOrThrow("manual") - - fun _json(): Optional = Optional.ofNullable(_json) - - fun accept(visitor: Visitor): T = - when { - auto != null -> visitor.visitAuto(auto) - manual != null -> visitor.visitManual(manual) - else -> visitor.unknown(_json) - } - - private var validated: Boolean = false - - fun validate(): LearningRateMultiplier = apply { - if (validated) { - return@apply - } - - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) { - auto.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - - override fun visitManual(manual: Double) {} - } - ) - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) = - auto.let { if (it == JsonValue.from("auto")) 1 else 0 } - - override fun visitManual(manual: Double) = 1 - - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is LearningRateMultiplier && auto == other.auto && manual == other.manual /* spotless:on */ - } - - override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual) /* spotless:on */ - - override fun toString(): String = - when { - auto != null -> "LearningRateMultiplier{auto=$auto}" - manual != null -> "LearningRateMultiplier{manual=$manual}" - _json != null -> "LearningRateMultiplier{_unknown=$_json}" - else -> throw IllegalStateException("Invalid LearningRateMultiplier") - } - - companion object { - - @JvmStatic - fun ofAuto() = LearningRateMultiplier(auto = JsonValue.from("auto")) - - @JvmStatic - fun ofManual(manual: Double) = LearningRateMultiplier(manual = manual) - } - - /** - * An interface that defines how to map each variant of [LearningRateMultiplier] - * to a value of type [T]. - */ - interface Visitor { - - fun visitAuto(auto: JsonValue): T - - fun visitManual(manual: Double): T - - /** - * Maps an unknown variant of [LearningRateMultiplier] to a value of type - * [T]. - * - * An instance of [LearningRateMultiplier] 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 LearningRateMultiplier: $json" - ) - } - } - - internal class Deserializer : - BaseDeserializer(LearningRateMultiplier::class) { - - override fun ObjectCodec.deserialize( - node: JsonNode - ): LearningRateMultiplier { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { - LearningRateMultiplier(auto = it, _json = json) - } - ?.takeIf { it.isValid() }, - tryDeserialize(node, jacksonTypeRef())?.let { - LearningRateMultiplier(manual = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from object). - 0 -> LearningRateMultiplier(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } - } - - internal class Serializer : - BaseSerializer(LearningRateMultiplier::class) { - - override fun serialize( - value: LearningRateMultiplier, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.auto != null -> generator.writeObject(value.auto) - value.manual != null -> generator.writeObject(value.manual) - value._json != null -> generator.writeObject(value._json) - else -> - throw IllegalStateException("Invalid LearningRateMultiplier") - } - } - } - } - - /** - * The number of epochs to train the model for. An epoch refers to one full cycle - * through the training dataset. - */ - @JsonDeserialize(using = NEpochs.Deserializer::class) - @JsonSerialize(using = NEpochs.Serializer::class) - class NEpochs - private constructor( - private val auto: JsonValue? = null, - private val manual: Long? = null, - private val _json: JsonValue? = null, - ) { - - fun auto(): Optional = Optional.ofNullable(auto) - - fun manual(): Optional = Optional.ofNullable(manual) - - fun isAuto(): Boolean = auto != null - - fun isManual(): Boolean = manual != null - - fun asAuto(): JsonValue = auto.getOrThrow("auto") - - fun asManual(): Long = manual.getOrThrow("manual") - - fun _json(): Optional = Optional.ofNullable(_json) - - fun accept(visitor: Visitor): T = - when { - auto != null -> visitor.visitAuto(auto) - manual != null -> visitor.visitManual(manual) - else -> visitor.unknown(_json) - } - - private var validated: Boolean = false - - fun validate(): NEpochs = apply { - if (validated) { - return@apply - } - - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) { - auto.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - - override fun visitManual(manual: Long) {} - } - ) - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) = - auto.let { if (it == JsonValue.from("auto")) 1 else 0 } - - override fun visitManual(manual: Long) = 1 - - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is NEpochs && auto == other.auto && manual == other.manual /* spotless:on */ - } - - override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual) /* spotless:on */ - - override fun toString(): String = - when { - auto != null -> "NEpochs{auto=$auto}" - manual != null -> "NEpochs{manual=$manual}" - _json != null -> "NEpochs{_unknown=$_json}" - else -> throw IllegalStateException("Invalid NEpochs") - } - - companion object { - - @JvmStatic fun ofAuto() = NEpochs(auto = JsonValue.from("auto")) - - @JvmStatic fun ofManual(manual: Long) = NEpochs(manual = manual) - } - - /** - * An interface that defines how to map each variant of [NEpochs] to a value of - * type [T]. - */ - interface Visitor { - - fun visitAuto(auto: JsonValue): T - - fun visitManual(manual: Long): T - - /** - * Maps an unknown variant of [NEpochs] to a value of type [T]. - * - * An instance of [NEpochs] 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 NEpochs: $json") - } - } - - internal class Deserializer : BaseDeserializer(NEpochs::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): NEpochs { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { NEpochs(auto = it, _json = json) } - ?.takeIf { it.isValid() }, - tryDeserialize(node, jacksonTypeRef())?.let { - NEpochs(manual = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from object). - 0 -> NEpochs(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } - } - - internal class Serializer : BaseSerializer(NEpochs::class) { - - override fun serialize( - value: NEpochs, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.auto != null -> generator.writeObject(value.auto) - value.manual != null -> generator.writeObject(value.manual) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid NEpochs") - } - } - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is Hyperparameters && batchSize == other.batchSize && learningRateMultiplier == other.learningRateMultiplier && nEpochs == other.nEpochs && additionalProperties == other.additionalProperties /* spotless:on */ - } - - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(batchSize, learningRateMultiplier, nEpochs, additionalProperties) } - /* spotless:on */ - - override fun hashCode(): Int = hashCode - - override fun toString() = - "Hyperparameters{batchSize=$batchSize, learningRateMultiplier=$learningRateMultiplier, nEpochs=$nEpochs, additionalProperties=$additionalProperties}" - } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is Supervised && hyperparameters == other.hyperparameters && additionalProperties == other.additionalProperties /* spotless:on */ - } - - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(hyperparameters, additionalProperties) } - /* spotless:on */ - - override fun hashCode(): Int = hashCode - - override fun toString() = - "Supervised{hyperparameters=$hyperparameters, additionalProperties=$additionalProperties}" - } - - /** The type of method. Is either `supervised` or `dpo`. */ - class Type @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 SUPERVISED = of("supervised") - - @JvmField val DPO = of("dpo") + @JvmField val REINFORCEMENT = of("reinforcement") @JvmStatic fun of(value: String) = Type(JsonField.of(value)) } @@ -4812,6 +2672,7 @@ private constructor( enum class Known { SUPERVISED, DPO, + REINFORCEMENT, } /** @@ -4826,6 +2687,7 @@ private constructor( enum class Value { SUPERVISED, DPO, + REINFORCEMENT, /** An enum member indicating that [Type] was instantiated with an unknown value. */ _UNKNOWN, } @@ -4841,6 +2703,7 @@ private constructor( when (this) { SUPERVISED -> Value.SUPERVISED DPO -> Value.DPO + REINFORCEMENT -> Value.REINFORCEMENT else -> Value._UNKNOWN } @@ -4857,6 +2720,7 @@ private constructor( when (this) { SUPERVISED -> Known.SUPERVISED DPO -> Known.DPO + REINFORCEMENT -> Known.REINFORCEMENT else -> throw OpenAIInvalidDataException("Unknown Type: $value") } @@ -4919,17 +2783,17 @@ private constructor( return true } - return /* spotless:off */ other is Method && dpo == other.dpo && supervised == other.supervised && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is Method && type == other.type && dpo == other.dpo && reinforcement == other.reinforcement && supervised == other.supervised && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(dpo, supervised, type, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(type, dpo, reinforcement, supervised, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "Method{dpo=$dpo, supervised=$supervised, type=$type, additionalProperties=$additionalProperties}" + "Method{type=$type, dpo=$dpo, reinforcement=$reinforcement, supervised=$supervised, additionalProperties=$additionalProperties}" } override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobCancelParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobCancelParams.kt index 4d7c9f96..a4fbd422 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobCancelParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobCancelParams.kt @@ -4,23 +4,23 @@ package com.openai.models.finetuning.jobs import com.openai.core.JsonValue 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 +import kotlin.jvm.optionals.getOrNull /** Immediately cancel a fine-tune job. */ class JobCancelParams private constructor( - private val fineTuningJobId: String, + private val fineTuningJobId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, ) : Params { - fun fineTuningJobId(): String = fineTuningJobId + fun fineTuningJobId(): Optional = Optional.ofNullable(fineTuningJobId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -32,14 +32,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [JobCancelParams]. - * - * The following fields are required: - * ```java - * .fineTuningJobId() - * ``` - */ + @JvmStatic fun none(): JobCancelParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [JobCancelParams]. */ @JvmStatic fun builder() = Builder() } @@ -59,10 +54,14 @@ private constructor( additionalBodyProperties = jobCancelParams.additionalBodyProperties.toMutableMap() } - fun fineTuningJobId(fineTuningJobId: String) = apply { + fun fineTuningJobId(fineTuningJobId: String?) = apply { this.fineTuningJobId = fineTuningJobId } + /** Alias for calling [Builder.fineTuningJobId] with `fineTuningJobId.orElse(null)`. */ + fun fineTuningJobId(fineTuningJobId: Optional) = + fineTuningJobId(fineTuningJobId.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -187,17 +186,10 @@ private constructor( * Returns an immutable instance of [JobCancelParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .fineTuningJobId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): JobCancelParams = JobCancelParams( - checkRequired("fineTuningJobId", fineTuningJobId), + fineTuningJobId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -209,7 +201,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> fineTuningJobId + 0 -> fineTuningJobId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobCreateParams.kt index 345624c2..f08fe5de 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobCreateParams.kt @@ -29,6 +29,9 @@ import com.openai.core.http.Headers import com.openai.core.http.QueryParams import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.finetuning.methods.DpoMethod +import com.openai.models.finetuning.methods.ReinforcementMethod +import com.openai.models.finetuning.methods.SupervisedMethod import java.util.Collections import java.util.Objects import java.util.Optional @@ -2835,20 +2838,32 @@ private constructor( /** The method used for fine-tuning. */ class Method private constructor( - private val dpo: JsonField, - private val supervised: JsonField, private val type: JsonField, + private val dpo: JsonField, + private val reinforcement: JsonField, + private val supervised: JsonField, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("dpo") @ExcludeMissing dpo: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + @JsonProperty("dpo") @ExcludeMissing dpo: JsonField = JsonMissing.of(), + @JsonProperty("reinforcement") + @ExcludeMissing + reinforcement: JsonField = JsonMissing.of(), @JsonProperty("supervised") @ExcludeMissing - supervised: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), - ) : this(dpo, supervised, type, mutableMapOf()) + supervised: JsonField = JsonMissing.of(), + ) : this(type, dpo, reinforcement, supervised, mutableMapOf()) + + /** + * The type of method. Is either `supervised`, `dpo`, or `reinforcement`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun type(): Type = type.getRequired("type") /** * Configuration for the DPO fine-tuning method. @@ -2856,46 +2871,57 @@ private constructor( * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ - fun dpo(): Optional = dpo.getOptional("dpo") + fun dpo(): Optional = dpo.getOptional("dpo") /** - * Configuration for the supervised fine-tuning method. + * Configuration for the reinforcement fine-tuning method. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ - fun supervised(): Optional = supervised.getOptional("supervised") + fun reinforcement(): Optional = + reinforcement.getOptional("reinforcement") /** - * The type of method. Is either `supervised` or `dpo`. + * Configuration for the supervised fine-tuning method. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ - fun type(): Optional = type.getOptional("type") + fun supervised(): Optional = supervised.getOptional("supervised") + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type /** * Returns the raw JSON value of [dpo]. * * Unlike [dpo], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("dpo") @ExcludeMissing fun _dpo(): JsonField = dpo + @JsonProperty("dpo") @ExcludeMissing fun _dpo(): JsonField = dpo /** - * Returns the raw JSON value of [supervised]. + * Returns the raw JSON value of [reinforcement]. * - * Unlike [supervised], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [reinforcement], this method doesn't throw if the JSON field has an unexpected + * type. */ - @JsonProperty("supervised") + @JsonProperty("reinforcement") @ExcludeMissing - fun _supervised(): JsonField = supervised + fun _reinforcement(): JsonField = reinforcement /** - * Returns the raw JSON value of [type]. + * Returns the raw JSON value of [supervised]. * - * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [supervised], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + @JsonProperty("supervised") + @ExcludeMissing + fun _supervised(): JsonField = supervised @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -2911,63 +2937,87 @@ private constructor( companion object { - /** Returns a mutable builder for constructing an instance of [Method]. */ + /** + * Returns a mutable builder for constructing an instance of [Method]. + * + * The following fields are required: + * ```java + * .type() + * ``` + */ @JvmStatic fun builder() = Builder() } /** A builder for [Method]. */ class Builder internal constructor() { - private var dpo: JsonField = JsonMissing.of() - private var supervised: JsonField = JsonMissing.of() - private var type: JsonField = JsonMissing.of() + private var type: JsonField? = null + private var dpo: JsonField = JsonMissing.of() + private var reinforcement: JsonField = JsonMissing.of() + private var supervised: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(method: Method) = apply { + type = method.type dpo = method.dpo + reinforcement = method.reinforcement supervised = method.supervised - type = method.type additionalProperties = method.additionalProperties.toMutableMap() } - /** Configuration for the DPO fine-tuning method. */ - fun dpo(dpo: Dpo) = dpo(JsonField.of(dpo)) + /** The type of method. Is either `supervised`, `dpo`, or `reinforcement`. */ + fun type(type: Type) = type(JsonField.of(type)) /** - * Sets [Builder.dpo] to an arbitrary JSON value. + * Sets [Builder.type] to an arbitrary JSON value. * - * You should usually call [Builder.dpo] with a well-typed [Dpo] value instead. This + * You should usually call [Builder.type] with a well-typed [Type] value instead. This * method is primarily for setting the field to an undocumented or not yet supported * value. */ - fun dpo(dpo: JsonField) = apply { this.dpo = dpo } + fun type(type: JsonField) = apply { this.type = type } - /** Configuration for the supervised fine-tuning method. */ - fun supervised(supervised: Supervised) = supervised(JsonField.of(supervised)) + /** Configuration for the DPO fine-tuning method. */ + fun dpo(dpo: DpoMethod) = dpo(JsonField.of(dpo)) /** - * Sets [Builder.supervised] to an arbitrary JSON value. + * Sets [Builder.dpo] to an arbitrary JSON value. * - * You should usually call [Builder.supervised] with a well-typed [Supervised] value - * instead. This method is primarily for setting the field to an undocumented or not yet + * You should usually call [Builder.dpo] with a well-typed [DpoMethod] value instead. + * This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun supervised(supervised: JsonField) = apply { - this.supervised = supervised + fun dpo(dpo: JsonField) = apply { this.dpo = dpo } + + /** Configuration for the reinforcement fine-tuning method. */ + fun reinforcement(reinforcement: ReinforcementMethod) = + reinforcement(JsonField.of(reinforcement)) + + /** + * Sets [Builder.reinforcement] to an arbitrary JSON value. + * + * You should usually call [Builder.reinforcement] with a well-typed + * [ReinforcementMethod] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun reinforcement(reinforcement: JsonField) = apply { + this.reinforcement = reinforcement } - /** The type of method. Is either `supervised` or `dpo`. */ - fun type(type: Type) = type(JsonField.of(type)) + /** Configuration for the supervised fine-tuning method. */ + fun supervised(supervised: SupervisedMethod) = supervised(JsonField.of(supervised)) /** - * Sets [Builder.type] to an arbitrary JSON value. + * Sets [Builder.supervised] to an arbitrary JSON value. * - * You should usually call [Builder.type] with a well-typed [Type] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported - * value. + * You should usually call [Builder.supervised] with a well-typed [SupervisedMethod] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. */ - fun type(type: JsonField) = apply { this.type = type } + fun supervised(supervised: JsonField) = apply { + this.supervised = supervised + } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -2992,8 +3042,22 @@ private constructor( * Returns an immutable instance of [Method]. * * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .type() + * ``` + * + * @throws IllegalStateException if any required field is unset. */ - fun build(): Method = Method(dpo, supervised, type, additionalProperties.toMutableMap()) + fun build(): Method = + Method( + checkRequired("type", type), + dpo, + reinforcement, + supervised, + additionalProperties.toMutableMap(), + ) } private var validated: Boolean = false @@ -3003,9 +3067,10 @@ private constructor( return@apply } + type().validate() dpo().ifPresent { it.validate() } + reinforcement().ifPresent { it.validate() } supervised().ifPresent { it.validate() } - type().ifPresent { it.validate() } validated = true } @@ -3025,2263 +3090,31 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (dpo.asKnown().getOrNull()?.validity() ?: 0) + - (supervised.asKnown().getOrNull()?.validity() ?: 0) + - (type.asKnown().getOrNull()?.validity() ?: 0) - - /** Configuration for the DPO fine-tuning method. */ - class Dpo - private constructor( - private val hyperparameters: JsonField, - private val additionalProperties: MutableMap, - ) { - - @JsonCreator - private constructor( - @JsonProperty("hyperparameters") - @ExcludeMissing - hyperparameters: JsonField = JsonMissing.of() - ) : this(hyperparameters, mutableMapOf()) + (type.asKnown().getOrNull()?.validity() ?: 0) + + (dpo.asKnown().getOrNull()?.validity() ?: 0) + + (reinforcement.asKnown().getOrNull()?.validity() ?: 0) + + (supervised.asKnown().getOrNull()?.validity() ?: 0) - /** - * The hyperparameters used for the fine-tuning job. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun hyperparameters(): Optional = - hyperparameters.getOptional("hyperparameters") + /** The type of method. Is either `supervised`, `dpo`, or `reinforcement`. */ + class Type @JsonCreator private constructor(private val value: JsonField) : Enum { /** - * Returns the raw JSON value of [hyperparameters]. + * Returns this class instance's raw value. * - * Unlike [hyperparameters], this method doesn't throw if the JSON field has an - * unexpected type. + * 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. */ - @JsonProperty("hyperparameters") - @ExcludeMissing - fun _hyperparameters(): JsonField = hyperparameters - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value companion object { - /** Returns a mutable builder for constructing an instance of [Dpo]. */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [Dpo]. */ - class Builder internal constructor() { - - private var hyperparameters: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(dpo: Dpo) = apply { - hyperparameters = dpo.hyperparameters - additionalProperties = dpo.additionalProperties.toMutableMap() - } - - /** The hyperparameters used for the fine-tuning job. */ - fun hyperparameters(hyperparameters: Hyperparameters) = - hyperparameters(JsonField.of(hyperparameters)) - - /** - * Sets [Builder.hyperparameters] to an arbitrary JSON value. - * - * You should usually call [Builder.hyperparameters] with a well-typed - * [Hyperparameters] value instead. This method is primarily for setting the field - * to an undocumented or not yet supported value. - */ - fun hyperparameters(hyperparameters: JsonField) = apply { - this.hyperparameters = hyperparameters - } - - 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) - } - - /** - * Returns an immutable instance of [Dpo]. - * - * Further updates to this [Builder] will not mutate the returned instance. - */ - fun build(): Dpo = Dpo(hyperparameters, additionalProperties.toMutableMap()) - } - - private var validated: Boolean = false - - fun validate(): Dpo = apply { - if (validated) { - return@apply - } - - hyperparameters().ifPresent { it.validate() } - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = (hyperparameters.asKnown().getOrNull()?.validity() ?: 0) - - /** The hyperparameters used for the fine-tuning job. */ - class Hyperparameters - private constructor( - private val batchSize: JsonField, - private val beta: JsonField, - private val learningRateMultiplier: JsonField, - private val nEpochs: JsonField, - private val additionalProperties: MutableMap, - ) { - - @JsonCreator - private constructor( - @JsonProperty("batch_size") - @ExcludeMissing - batchSize: JsonField = JsonMissing.of(), - @JsonProperty("beta") @ExcludeMissing beta: JsonField = JsonMissing.of(), - @JsonProperty("learning_rate_multiplier") - @ExcludeMissing - learningRateMultiplier: JsonField = JsonMissing.of(), - @JsonProperty("n_epochs") - @ExcludeMissing - nEpochs: JsonField = JsonMissing.of(), - ) : this(batchSize, beta, learningRateMultiplier, nEpochs, mutableMapOf()) - - /** - * Number of examples in each batch. A larger batch size means that model parameters - * are updated less frequently, but with lower variance. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun batchSize(): Optional = batchSize.getOptional("batch_size") - - /** - * The beta value for the DPO method. A higher beta value will increase the weight - * of the penalty between the policy and reference model. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun beta(): Optional = beta.getOptional("beta") - - /** - * Scaling factor for the learning rate. A smaller learning rate may be useful to - * avoid overfitting. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun learningRateMultiplier(): Optional = - learningRateMultiplier.getOptional("learning_rate_multiplier") - - /** - * The number of epochs to train the model for. An epoch refers to one full cycle - * through the training dataset. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun nEpochs(): Optional = nEpochs.getOptional("n_epochs") - - /** - * Returns the raw JSON value of [batchSize]. - * - * Unlike [batchSize], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("batch_size") - @ExcludeMissing - fun _batchSize(): JsonField = batchSize - - /** - * Returns the raw JSON value of [beta]. - * - * Unlike [beta], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("beta") @ExcludeMissing fun _beta(): JsonField = beta - - /** - * Returns the raw JSON value of [learningRateMultiplier]. - * - * Unlike [learningRateMultiplier], this method doesn't throw if the JSON field has - * an unexpected type. - */ - @JsonProperty("learning_rate_multiplier") - @ExcludeMissing - fun _learningRateMultiplier(): JsonField = - learningRateMultiplier - - /** - * Returns the raw JSON value of [nEpochs]. - * - * Unlike [nEpochs], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("n_epochs") - @ExcludeMissing - fun _nEpochs(): JsonField = nEpochs - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { - - /** - * Returns a mutable builder for constructing an instance of [Hyperparameters]. - */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [Hyperparameters]. */ - class Builder internal constructor() { - - private var batchSize: JsonField = JsonMissing.of() - private var beta: JsonField = JsonMissing.of() - private var learningRateMultiplier: JsonField = - JsonMissing.of() - private var nEpochs: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(hyperparameters: Hyperparameters) = apply { - batchSize = hyperparameters.batchSize - beta = hyperparameters.beta - learningRateMultiplier = hyperparameters.learningRateMultiplier - nEpochs = hyperparameters.nEpochs - additionalProperties = hyperparameters.additionalProperties.toMutableMap() - } - - /** - * Number of examples in each batch. A larger batch size means that model - * parameters are updated less frequently, but with lower variance. - */ - fun batchSize(batchSize: BatchSize) = batchSize(JsonField.of(batchSize)) - - /** - * Sets [Builder.batchSize] to an arbitrary JSON value. - * - * You should usually call [Builder.batchSize] with a well-typed [BatchSize] - * value instead. This method is primarily for setting the field to an - * undocumented or not yet supported value. - */ - fun batchSize(batchSize: JsonField) = apply { - this.batchSize = batchSize - } - - /** Alias for calling [batchSize] with `BatchSize.ofAuto()`. */ - fun batchSizeAuto() = batchSize(BatchSize.ofAuto()) - - /** Alias for calling [batchSize] with `BatchSize.ofManual(manual)`. */ - fun batchSize(manual: Long) = batchSize(BatchSize.ofManual(manual)) - - /** - * The beta value for the DPO method. A higher beta value will increase the - * weight of the penalty between the policy and reference model. - */ - fun beta(beta: Beta) = beta(JsonField.of(beta)) - - /** - * Sets [Builder.beta] to an arbitrary JSON value. - * - * You should usually call [Builder.beta] with a well-typed [Beta] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun beta(beta: JsonField) = apply { this.beta = beta } - - /** Alias for calling [beta] with `Beta.ofAuto()`. */ - fun betaAuto() = beta(Beta.ofAuto()) - - /** Alias for calling [beta] with `Beta.ofManual(manual)`. */ - fun beta(manual: Double) = beta(Beta.ofManual(manual)) - - /** - * Scaling factor for the learning rate. A smaller learning rate may be useful - * to avoid overfitting. - */ - fun learningRateMultiplier(learningRateMultiplier: LearningRateMultiplier) = - learningRateMultiplier(JsonField.of(learningRateMultiplier)) - - /** - * Sets [Builder.learningRateMultiplier] to an arbitrary JSON value. - * - * You should usually call [Builder.learningRateMultiplier] with a well-typed - * [LearningRateMultiplier] value instead. This method is primarily for setting - * the field to an undocumented or not yet supported value. - */ - fun learningRateMultiplier( - learningRateMultiplier: JsonField - ) = apply { this.learningRateMultiplier = learningRateMultiplier } - - /** - * Alias for calling [learningRateMultiplier] with - * `LearningRateMultiplier.ofAuto()`. - */ - fun learningRateMultiplierAuto() = - learningRateMultiplier(LearningRateMultiplier.ofAuto()) - - /** - * Alias for calling [learningRateMultiplier] with - * `LearningRateMultiplier.ofManual(manual)`. - */ - fun learningRateMultiplier(manual: Double) = - learningRateMultiplier(LearningRateMultiplier.ofManual(manual)) - - /** - * The number of epochs to train the model for. An epoch refers to one full - * cycle through the training dataset. - */ - fun nEpochs(nEpochs: NEpochs) = nEpochs(JsonField.of(nEpochs)) - - /** - * Sets [Builder.nEpochs] to an arbitrary JSON value. - * - * You should usually call [Builder.nEpochs] with a well-typed [NEpochs] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun nEpochs(nEpochs: JsonField) = apply { this.nEpochs = nEpochs } - - /** Alias for calling [nEpochs] with `NEpochs.ofAuto()`. */ - fun nEpochsAuto() = nEpochs(NEpochs.ofAuto()) - - /** Alias for calling [nEpochs] with `NEpochs.ofManual(manual)`. */ - fun nEpochs(manual: Long) = nEpochs(NEpochs.ofManual(manual)) - - 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) - } - - /** - * Returns an immutable instance of [Hyperparameters]. - * - * Further updates to this [Builder] will not mutate the returned instance. - */ - fun build(): Hyperparameters = - Hyperparameters( - batchSize, - beta, - learningRateMultiplier, - nEpochs, - additionalProperties.toMutableMap(), - ) - } - - private var validated: Boolean = false - - fun validate(): Hyperparameters = apply { - if (validated) { - return@apply - } - - batchSize().ifPresent { it.validate() } - beta().ifPresent { it.validate() } - learningRateMultiplier().ifPresent { it.validate() } - nEpochs().ifPresent { it.validate() } - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (batchSize.asKnown().getOrNull()?.validity() ?: 0) + - (beta.asKnown().getOrNull()?.validity() ?: 0) + - (learningRateMultiplier.asKnown().getOrNull()?.validity() ?: 0) + - (nEpochs.asKnown().getOrNull()?.validity() ?: 0) + @JvmField val SUPERVISED = of("supervised") - /** - * Number of examples in each batch. A larger batch size means that model parameters - * are updated less frequently, but with lower variance. - */ - @JsonDeserialize(using = BatchSize.Deserializer::class) - @JsonSerialize(using = BatchSize.Serializer::class) - class BatchSize - private constructor( - private val auto: JsonValue? = null, - private val manual: Long? = null, - private val _json: JsonValue? = null, - ) { + @JvmField val DPO = of("dpo") - fun auto(): Optional = Optional.ofNullable(auto) - - fun manual(): Optional = Optional.ofNullable(manual) - - fun isAuto(): Boolean = auto != null - - fun isManual(): Boolean = manual != null - - fun asAuto(): JsonValue = auto.getOrThrow("auto") - - fun asManual(): Long = manual.getOrThrow("manual") - - fun _json(): Optional = Optional.ofNullable(_json) - - fun accept(visitor: Visitor): T = - when { - auto != null -> visitor.visitAuto(auto) - manual != null -> visitor.visitManual(manual) - else -> visitor.unknown(_json) - } - - private var validated: Boolean = false - - fun validate(): BatchSize = apply { - if (validated) { - return@apply - } - - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) { - auto.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - - override fun visitManual(manual: Long) {} - } - ) - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) = - auto.let { if (it == JsonValue.from("auto")) 1 else 0 } - - override fun visitManual(manual: Long) = 1 - - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is BatchSize && auto == other.auto && manual == other.manual /* spotless:on */ - } - - override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual) /* spotless:on */ - - override fun toString(): String = - when { - auto != null -> "BatchSize{auto=$auto}" - manual != null -> "BatchSize{manual=$manual}" - _json != null -> "BatchSize{_unknown=$_json}" - else -> throw IllegalStateException("Invalid BatchSize") - } - - companion object { - - @JvmStatic fun ofAuto() = BatchSize(auto = JsonValue.from("auto")) - - @JvmStatic fun ofManual(manual: Long) = BatchSize(manual = manual) - } - - /** - * An interface that defines how to map each variant of [BatchSize] to a value - * of type [T]. - */ - interface Visitor { - - fun visitAuto(auto: JsonValue): T - - fun visitManual(manual: Long): T - - /** - * Maps an unknown variant of [BatchSize] to a value of type [T]. - * - * An instance of [BatchSize] 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 BatchSize: $json") - } - } - - internal class Deserializer : BaseDeserializer(BatchSize::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): BatchSize { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { BatchSize(auto = it, _json = json) } - ?.takeIf { it.isValid() }, - tryDeserialize(node, jacksonTypeRef())?.let { - BatchSize(manual = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from object). - 0 -> BatchSize(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } - } - - internal class Serializer : BaseSerializer(BatchSize::class) { - - override fun serialize( - value: BatchSize, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.auto != null -> generator.writeObject(value.auto) - value.manual != null -> generator.writeObject(value.manual) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid BatchSize") - } - } - } - } - - /** - * The beta value for the DPO method. A higher beta value will increase the weight - * of the penalty between the policy and reference model. - */ - @JsonDeserialize(using = Beta.Deserializer::class) - @JsonSerialize(using = Beta.Serializer::class) - class Beta - private constructor( - private val auto: JsonValue? = null, - private val manual: Double? = null, - private val _json: JsonValue? = null, - ) { - - fun auto(): Optional = Optional.ofNullable(auto) - - fun manual(): Optional = Optional.ofNullable(manual) - - fun isAuto(): Boolean = auto != null - - fun isManual(): Boolean = manual != null - - fun asAuto(): JsonValue = auto.getOrThrow("auto") - - fun asManual(): Double = manual.getOrThrow("manual") - - fun _json(): Optional = Optional.ofNullable(_json) - - fun accept(visitor: Visitor): T = - when { - auto != null -> visitor.visitAuto(auto) - manual != null -> visitor.visitManual(manual) - else -> visitor.unknown(_json) - } - - private var validated: Boolean = false - - fun validate(): Beta = apply { - if (validated) { - return@apply - } - - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) { - auto.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - - override fun visitManual(manual: Double) {} - } - ) - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) = - auto.let { if (it == JsonValue.from("auto")) 1 else 0 } - - override fun visitManual(manual: Double) = 1 - - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is Beta && auto == other.auto && manual == other.manual /* spotless:on */ - } - - override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual) /* spotless:on */ - - override fun toString(): String = - when { - auto != null -> "Beta{auto=$auto}" - manual != null -> "Beta{manual=$manual}" - _json != null -> "Beta{_unknown=$_json}" - else -> throw IllegalStateException("Invalid Beta") - } - - companion object { - - @JvmStatic fun ofAuto() = Beta(auto = JsonValue.from("auto")) - - @JvmStatic fun ofManual(manual: Double) = Beta(manual = manual) - } - - /** - * An interface that defines how to map each variant of [Beta] to a value of - * type [T]. - */ - interface Visitor { - - fun visitAuto(auto: JsonValue): T - - fun visitManual(manual: Double): T - - /** - * Maps an unknown variant of [Beta] to a value of type [T]. - * - * An instance of [Beta] 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 Beta: $json") - } - } - - internal class Deserializer : BaseDeserializer(Beta::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): Beta { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { Beta(auto = it, _json = json) } - ?.takeIf { it.isValid() }, - tryDeserialize(node, jacksonTypeRef())?.let { - Beta(manual = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from object). - 0 -> Beta(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } - } - - internal class Serializer : BaseSerializer(Beta::class) { - - override fun serialize( - value: Beta, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.auto != null -> generator.writeObject(value.auto) - value.manual != null -> generator.writeObject(value.manual) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid Beta") - } - } - } - } - - /** - * Scaling factor for the learning rate. A smaller learning rate may be useful to - * avoid overfitting. - */ - @JsonDeserialize(using = LearningRateMultiplier.Deserializer::class) - @JsonSerialize(using = LearningRateMultiplier.Serializer::class) - class LearningRateMultiplier - private constructor( - private val auto: JsonValue? = null, - private val manual: Double? = null, - private val _json: JsonValue? = null, - ) { - - fun auto(): Optional = Optional.ofNullable(auto) - - fun manual(): Optional = Optional.ofNullable(manual) - - fun isAuto(): Boolean = auto != null - - fun isManual(): Boolean = manual != null - - fun asAuto(): JsonValue = auto.getOrThrow("auto") - - fun asManual(): Double = manual.getOrThrow("manual") - - fun _json(): Optional = Optional.ofNullable(_json) - - fun accept(visitor: Visitor): T = - when { - auto != null -> visitor.visitAuto(auto) - manual != null -> visitor.visitManual(manual) - else -> visitor.unknown(_json) - } - - private var validated: Boolean = false - - fun validate(): LearningRateMultiplier = apply { - if (validated) { - return@apply - } - - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) { - auto.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - - override fun visitManual(manual: Double) {} - } - ) - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) = - auto.let { if (it == JsonValue.from("auto")) 1 else 0 } - - override fun visitManual(manual: Double) = 1 - - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is LearningRateMultiplier && auto == other.auto && manual == other.manual /* spotless:on */ - } - - override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual) /* spotless:on */ - - override fun toString(): String = - when { - auto != null -> "LearningRateMultiplier{auto=$auto}" - manual != null -> "LearningRateMultiplier{manual=$manual}" - _json != null -> "LearningRateMultiplier{_unknown=$_json}" - else -> throw IllegalStateException("Invalid LearningRateMultiplier") - } - - companion object { - - @JvmStatic - fun ofAuto() = LearningRateMultiplier(auto = JsonValue.from("auto")) - - @JvmStatic - fun ofManual(manual: Double) = LearningRateMultiplier(manual = manual) - } - - /** - * An interface that defines how to map each variant of [LearningRateMultiplier] - * to a value of type [T]. - */ - interface Visitor { - - fun visitAuto(auto: JsonValue): T - - fun visitManual(manual: Double): T - - /** - * Maps an unknown variant of [LearningRateMultiplier] to a value of type - * [T]. - * - * An instance of [LearningRateMultiplier] 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 LearningRateMultiplier: $json" - ) - } - } - - internal class Deserializer : - BaseDeserializer(LearningRateMultiplier::class) { - - override fun ObjectCodec.deserialize( - node: JsonNode - ): LearningRateMultiplier { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { - LearningRateMultiplier(auto = it, _json = json) - } - ?.takeIf { it.isValid() }, - tryDeserialize(node, jacksonTypeRef())?.let { - LearningRateMultiplier(manual = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from object). - 0 -> LearningRateMultiplier(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } - } - - internal class Serializer : - BaseSerializer(LearningRateMultiplier::class) { - - override fun serialize( - value: LearningRateMultiplier, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.auto != null -> generator.writeObject(value.auto) - value.manual != null -> generator.writeObject(value.manual) - value._json != null -> generator.writeObject(value._json) - else -> - throw IllegalStateException("Invalid LearningRateMultiplier") - } - } - } - } - - /** - * The number of epochs to train the model for. An epoch refers to one full cycle - * through the training dataset. - */ - @JsonDeserialize(using = NEpochs.Deserializer::class) - @JsonSerialize(using = NEpochs.Serializer::class) - class NEpochs - private constructor( - private val auto: JsonValue? = null, - private val manual: Long? = null, - private val _json: JsonValue? = null, - ) { - - fun auto(): Optional = Optional.ofNullable(auto) - - fun manual(): Optional = Optional.ofNullable(manual) - - fun isAuto(): Boolean = auto != null - - fun isManual(): Boolean = manual != null - - fun asAuto(): JsonValue = auto.getOrThrow("auto") - - fun asManual(): Long = manual.getOrThrow("manual") - - fun _json(): Optional = Optional.ofNullable(_json) - - fun accept(visitor: Visitor): T = - when { - auto != null -> visitor.visitAuto(auto) - manual != null -> visitor.visitManual(manual) - else -> visitor.unknown(_json) - } - - private var validated: Boolean = false - - fun validate(): NEpochs = apply { - if (validated) { - return@apply - } - - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) { - auto.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - - override fun visitManual(manual: Long) {} - } - ) - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) = - auto.let { if (it == JsonValue.from("auto")) 1 else 0 } - - override fun visitManual(manual: Long) = 1 - - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is NEpochs && auto == other.auto && manual == other.manual /* spotless:on */ - } - - override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual) /* spotless:on */ - - override fun toString(): String = - when { - auto != null -> "NEpochs{auto=$auto}" - manual != null -> "NEpochs{manual=$manual}" - _json != null -> "NEpochs{_unknown=$_json}" - else -> throw IllegalStateException("Invalid NEpochs") - } - - companion object { - - @JvmStatic fun ofAuto() = NEpochs(auto = JsonValue.from("auto")) - - @JvmStatic fun ofManual(manual: Long) = NEpochs(manual = manual) - } - - /** - * An interface that defines how to map each variant of [NEpochs] to a value of - * type [T]. - */ - interface Visitor { - - fun visitAuto(auto: JsonValue): T - - fun visitManual(manual: Long): T - - /** - * Maps an unknown variant of [NEpochs] to a value of type [T]. - * - * An instance of [NEpochs] 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 NEpochs: $json") - } - } - - internal class Deserializer : BaseDeserializer(NEpochs::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): NEpochs { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { NEpochs(auto = it, _json = json) } - ?.takeIf { it.isValid() }, - tryDeserialize(node, jacksonTypeRef())?.let { - NEpochs(manual = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from object). - 0 -> NEpochs(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } - } - - internal class Serializer : BaseSerializer(NEpochs::class) { - - override fun serialize( - value: NEpochs, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.auto != null -> generator.writeObject(value.auto) - value.manual != null -> generator.writeObject(value.manual) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid NEpochs") - } - } - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is Hyperparameters && batchSize == other.batchSize && beta == other.beta && learningRateMultiplier == other.learningRateMultiplier && nEpochs == other.nEpochs && additionalProperties == other.additionalProperties /* spotless:on */ - } - - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(batchSize, beta, learningRateMultiplier, nEpochs, additionalProperties) } - /* spotless:on */ - - override fun hashCode(): Int = hashCode - - override fun toString() = - "Hyperparameters{batchSize=$batchSize, beta=$beta, learningRateMultiplier=$learningRateMultiplier, nEpochs=$nEpochs, additionalProperties=$additionalProperties}" - } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is Dpo && hyperparameters == other.hyperparameters && additionalProperties == other.additionalProperties /* spotless:on */ - } - - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(hyperparameters, additionalProperties) } - /* spotless:on */ - - override fun hashCode(): Int = hashCode - - override fun toString() = - "Dpo{hyperparameters=$hyperparameters, additionalProperties=$additionalProperties}" - } - - /** Configuration for the supervised fine-tuning method. */ - class Supervised - private constructor( - private val hyperparameters: JsonField, - private val additionalProperties: MutableMap, - ) { - - @JsonCreator - private constructor( - @JsonProperty("hyperparameters") - @ExcludeMissing - hyperparameters: JsonField = JsonMissing.of() - ) : this(hyperparameters, mutableMapOf()) - - /** - * The hyperparameters used for the fine-tuning job. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun hyperparameters(): Optional = - hyperparameters.getOptional("hyperparameters") - - /** - * Returns the raw JSON value of [hyperparameters]. - * - * Unlike [hyperparameters], this method doesn't throw if the JSON field has an - * unexpected type. - */ - @JsonProperty("hyperparameters") - @ExcludeMissing - fun _hyperparameters(): JsonField = hyperparameters - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { - - /** Returns a mutable builder for constructing an instance of [Supervised]. */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [Supervised]. */ - class Builder internal constructor() { - - private var hyperparameters: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(supervised: Supervised) = apply { - hyperparameters = supervised.hyperparameters - additionalProperties = supervised.additionalProperties.toMutableMap() - } - - /** The hyperparameters used for the fine-tuning job. */ - fun hyperparameters(hyperparameters: Hyperparameters) = - hyperparameters(JsonField.of(hyperparameters)) - - /** - * Sets [Builder.hyperparameters] to an arbitrary JSON value. - * - * You should usually call [Builder.hyperparameters] with a well-typed - * [Hyperparameters] value instead. This method is primarily for setting the field - * to an undocumented or not yet supported value. - */ - fun hyperparameters(hyperparameters: JsonField) = apply { - this.hyperparameters = hyperparameters - } - - 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) - } - - /** - * Returns an immutable instance of [Supervised]. - * - * Further updates to this [Builder] will not mutate the returned instance. - */ - fun build(): Supervised = - Supervised(hyperparameters, additionalProperties.toMutableMap()) - } - - private var validated: Boolean = false - - fun validate(): Supervised = apply { - if (validated) { - return@apply - } - - hyperparameters().ifPresent { it.validate() } - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = (hyperparameters.asKnown().getOrNull()?.validity() ?: 0) - - /** The hyperparameters used for the fine-tuning job. */ - class Hyperparameters - private constructor( - private val batchSize: JsonField, - private val learningRateMultiplier: JsonField, - private val nEpochs: JsonField, - private val additionalProperties: MutableMap, - ) { - - @JsonCreator - private constructor( - @JsonProperty("batch_size") - @ExcludeMissing - batchSize: JsonField = JsonMissing.of(), - @JsonProperty("learning_rate_multiplier") - @ExcludeMissing - learningRateMultiplier: JsonField = JsonMissing.of(), - @JsonProperty("n_epochs") - @ExcludeMissing - nEpochs: JsonField = JsonMissing.of(), - ) : this(batchSize, learningRateMultiplier, nEpochs, mutableMapOf()) - - /** - * Number of examples in each batch. A larger batch size means that model parameters - * are updated less frequently, but with lower variance. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun batchSize(): Optional = batchSize.getOptional("batch_size") - - /** - * Scaling factor for the learning rate. A smaller learning rate may be useful to - * avoid overfitting. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun learningRateMultiplier(): Optional = - learningRateMultiplier.getOptional("learning_rate_multiplier") - - /** - * The number of epochs to train the model for. An epoch refers to one full cycle - * through the training dataset. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). - */ - fun nEpochs(): Optional = nEpochs.getOptional("n_epochs") - - /** - * Returns the raw JSON value of [batchSize]. - * - * Unlike [batchSize], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("batch_size") - @ExcludeMissing - fun _batchSize(): JsonField = batchSize - - /** - * Returns the raw JSON value of [learningRateMultiplier]. - * - * Unlike [learningRateMultiplier], this method doesn't throw if the JSON field has - * an unexpected type. - */ - @JsonProperty("learning_rate_multiplier") - @ExcludeMissing - fun _learningRateMultiplier(): JsonField = - learningRateMultiplier - - /** - * Returns the raw JSON value of [nEpochs]. - * - * Unlike [nEpochs], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("n_epochs") - @ExcludeMissing - fun _nEpochs(): JsonField = nEpochs - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } - - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) - - fun toBuilder() = Builder().from(this) - - companion object { - - /** - * Returns a mutable builder for constructing an instance of [Hyperparameters]. - */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [Hyperparameters]. */ - class Builder internal constructor() { - - private var batchSize: JsonField = JsonMissing.of() - private var learningRateMultiplier: JsonField = - JsonMissing.of() - private var nEpochs: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(hyperparameters: Hyperparameters) = apply { - batchSize = hyperparameters.batchSize - learningRateMultiplier = hyperparameters.learningRateMultiplier - nEpochs = hyperparameters.nEpochs - additionalProperties = hyperparameters.additionalProperties.toMutableMap() - } - - /** - * Number of examples in each batch. A larger batch size means that model - * parameters are updated less frequently, but with lower variance. - */ - fun batchSize(batchSize: BatchSize) = batchSize(JsonField.of(batchSize)) - - /** - * Sets [Builder.batchSize] to an arbitrary JSON value. - * - * You should usually call [Builder.batchSize] with a well-typed [BatchSize] - * value instead. This method is primarily for setting the field to an - * undocumented or not yet supported value. - */ - fun batchSize(batchSize: JsonField) = apply { - this.batchSize = batchSize - } - - /** Alias for calling [batchSize] with `BatchSize.ofAuto()`. */ - fun batchSizeAuto() = batchSize(BatchSize.ofAuto()) - - /** Alias for calling [batchSize] with `BatchSize.ofManual(manual)`. */ - fun batchSize(manual: Long) = batchSize(BatchSize.ofManual(manual)) - - /** - * Scaling factor for the learning rate. A smaller learning rate may be useful - * to avoid overfitting. - */ - fun learningRateMultiplier(learningRateMultiplier: LearningRateMultiplier) = - learningRateMultiplier(JsonField.of(learningRateMultiplier)) - - /** - * Sets [Builder.learningRateMultiplier] to an arbitrary JSON value. - * - * You should usually call [Builder.learningRateMultiplier] with a well-typed - * [LearningRateMultiplier] value instead. This method is primarily for setting - * the field to an undocumented or not yet supported value. - */ - fun learningRateMultiplier( - learningRateMultiplier: JsonField - ) = apply { this.learningRateMultiplier = learningRateMultiplier } - - /** - * Alias for calling [learningRateMultiplier] with - * `LearningRateMultiplier.ofAuto()`. - */ - fun learningRateMultiplierAuto() = - learningRateMultiplier(LearningRateMultiplier.ofAuto()) - - /** - * Alias for calling [learningRateMultiplier] with - * `LearningRateMultiplier.ofManual(manual)`. - */ - fun learningRateMultiplier(manual: Double) = - learningRateMultiplier(LearningRateMultiplier.ofManual(manual)) - - /** - * The number of epochs to train the model for. An epoch refers to one full - * cycle through the training dataset. - */ - fun nEpochs(nEpochs: NEpochs) = nEpochs(JsonField.of(nEpochs)) - - /** - * Sets [Builder.nEpochs] to an arbitrary JSON value. - * - * You should usually call [Builder.nEpochs] with a well-typed [NEpochs] value - * instead. This method is primarily for setting the field to an undocumented or - * not yet supported value. - */ - fun nEpochs(nEpochs: JsonField) = apply { this.nEpochs = nEpochs } - - /** Alias for calling [nEpochs] with `NEpochs.ofAuto()`. */ - fun nEpochsAuto() = nEpochs(NEpochs.ofAuto()) - - /** Alias for calling [nEpochs] with `NEpochs.ofManual(manual)`. */ - fun nEpochs(manual: Long) = nEpochs(NEpochs.ofManual(manual)) - - 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) - } - - /** - * Returns an immutable instance of [Hyperparameters]. - * - * Further updates to this [Builder] will not mutate the returned instance. - */ - fun build(): Hyperparameters = - Hyperparameters( - batchSize, - learningRateMultiplier, - nEpochs, - additionalProperties.toMutableMap(), - ) - } - - private var validated: Boolean = false - - fun validate(): Hyperparameters = apply { - if (validated) { - return@apply - } - - batchSize().ifPresent { it.validate() } - learningRateMultiplier().ifPresent { it.validate() } - nEpochs().ifPresent { it.validate() } - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - (batchSize.asKnown().getOrNull()?.validity() ?: 0) + - (learningRateMultiplier.asKnown().getOrNull()?.validity() ?: 0) + - (nEpochs.asKnown().getOrNull()?.validity() ?: 0) - - /** - * Number of examples in each batch. A larger batch size means that model parameters - * are updated less frequently, but with lower variance. - */ - @JsonDeserialize(using = BatchSize.Deserializer::class) - @JsonSerialize(using = BatchSize.Serializer::class) - class BatchSize - private constructor( - private val auto: JsonValue? = null, - private val manual: Long? = null, - private val _json: JsonValue? = null, - ) { - - fun auto(): Optional = Optional.ofNullable(auto) - - fun manual(): Optional = Optional.ofNullable(manual) - - fun isAuto(): Boolean = auto != null - - fun isManual(): Boolean = manual != null - - fun asAuto(): JsonValue = auto.getOrThrow("auto") - - fun asManual(): Long = manual.getOrThrow("manual") - - fun _json(): Optional = Optional.ofNullable(_json) - - fun accept(visitor: Visitor): T = - when { - auto != null -> visitor.visitAuto(auto) - manual != null -> visitor.visitManual(manual) - else -> visitor.unknown(_json) - } - - private var validated: Boolean = false - - fun validate(): BatchSize = apply { - if (validated) { - return@apply - } - - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) { - auto.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - - override fun visitManual(manual: Long) {} - } - ) - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) = - auto.let { if (it == JsonValue.from("auto")) 1 else 0 } - - override fun visitManual(manual: Long) = 1 - - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is BatchSize && auto == other.auto && manual == other.manual /* spotless:on */ - } - - override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual) /* spotless:on */ - - override fun toString(): String = - when { - auto != null -> "BatchSize{auto=$auto}" - manual != null -> "BatchSize{manual=$manual}" - _json != null -> "BatchSize{_unknown=$_json}" - else -> throw IllegalStateException("Invalid BatchSize") - } - - companion object { - - @JvmStatic fun ofAuto() = BatchSize(auto = JsonValue.from("auto")) - - @JvmStatic fun ofManual(manual: Long) = BatchSize(manual = manual) - } - - /** - * An interface that defines how to map each variant of [BatchSize] to a value - * of type [T]. - */ - interface Visitor { - - fun visitAuto(auto: JsonValue): T - - fun visitManual(manual: Long): T - - /** - * Maps an unknown variant of [BatchSize] to a value of type [T]. - * - * An instance of [BatchSize] 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 BatchSize: $json") - } - } - - internal class Deserializer : BaseDeserializer(BatchSize::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): BatchSize { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { BatchSize(auto = it, _json = json) } - ?.takeIf { it.isValid() }, - tryDeserialize(node, jacksonTypeRef())?.let { - BatchSize(manual = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from object). - 0 -> BatchSize(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } - } - - internal class Serializer : BaseSerializer(BatchSize::class) { - - override fun serialize( - value: BatchSize, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.auto != null -> generator.writeObject(value.auto) - value.manual != null -> generator.writeObject(value.manual) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid BatchSize") - } - } - } - } - - /** - * Scaling factor for the learning rate. A smaller learning rate may be useful to - * avoid overfitting. - */ - @JsonDeserialize(using = LearningRateMultiplier.Deserializer::class) - @JsonSerialize(using = LearningRateMultiplier.Serializer::class) - class LearningRateMultiplier - private constructor( - private val auto: JsonValue? = null, - private val manual: Double? = null, - private val _json: JsonValue? = null, - ) { - - fun auto(): Optional = Optional.ofNullable(auto) - - fun manual(): Optional = Optional.ofNullable(manual) - - fun isAuto(): Boolean = auto != null - - fun isManual(): Boolean = manual != null - - fun asAuto(): JsonValue = auto.getOrThrow("auto") - - fun asManual(): Double = manual.getOrThrow("manual") - - fun _json(): Optional = Optional.ofNullable(_json) - - fun accept(visitor: Visitor): T = - when { - auto != null -> visitor.visitAuto(auto) - manual != null -> visitor.visitManual(manual) - else -> visitor.unknown(_json) - } - - private var validated: Boolean = false - - fun validate(): LearningRateMultiplier = apply { - if (validated) { - return@apply - } - - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) { - auto.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - - override fun visitManual(manual: Double) {} - } - ) - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) = - auto.let { if (it == JsonValue.from("auto")) 1 else 0 } - - override fun visitManual(manual: Double) = 1 - - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is LearningRateMultiplier && auto == other.auto && manual == other.manual /* spotless:on */ - } - - override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual) /* spotless:on */ - - override fun toString(): String = - when { - auto != null -> "LearningRateMultiplier{auto=$auto}" - manual != null -> "LearningRateMultiplier{manual=$manual}" - _json != null -> "LearningRateMultiplier{_unknown=$_json}" - else -> throw IllegalStateException("Invalid LearningRateMultiplier") - } - - companion object { - - @JvmStatic - fun ofAuto() = LearningRateMultiplier(auto = JsonValue.from("auto")) - - @JvmStatic - fun ofManual(manual: Double) = LearningRateMultiplier(manual = manual) - } - - /** - * An interface that defines how to map each variant of [LearningRateMultiplier] - * to a value of type [T]. - */ - interface Visitor { - - fun visitAuto(auto: JsonValue): T - - fun visitManual(manual: Double): T - - /** - * Maps an unknown variant of [LearningRateMultiplier] to a value of type - * [T]. - * - * An instance of [LearningRateMultiplier] 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 LearningRateMultiplier: $json" - ) - } - } - - internal class Deserializer : - BaseDeserializer(LearningRateMultiplier::class) { - - override fun ObjectCodec.deserialize( - node: JsonNode - ): LearningRateMultiplier { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { - LearningRateMultiplier(auto = it, _json = json) - } - ?.takeIf { it.isValid() }, - tryDeserialize(node, jacksonTypeRef())?.let { - LearningRateMultiplier(manual = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from object). - 0 -> LearningRateMultiplier(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } - } - - internal class Serializer : - BaseSerializer(LearningRateMultiplier::class) { - - override fun serialize( - value: LearningRateMultiplier, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.auto != null -> generator.writeObject(value.auto) - value.manual != null -> generator.writeObject(value.manual) - value._json != null -> generator.writeObject(value._json) - else -> - throw IllegalStateException("Invalid LearningRateMultiplier") - } - } - } - } - - /** - * The number of epochs to train the model for. An epoch refers to one full cycle - * through the training dataset. - */ - @JsonDeserialize(using = NEpochs.Deserializer::class) - @JsonSerialize(using = NEpochs.Serializer::class) - class NEpochs - private constructor( - private val auto: JsonValue? = null, - private val manual: Long? = null, - private val _json: JsonValue? = null, - ) { - - fun auto(): Optional = Optional.ofNullable(auto) - - fun manual(): Optional = Optional.ofNullable(manual) - - fun isAuto(): Boolean = auto != null - - fun isManual(): Boolean = manual != null - - fun asAuto(): JsonValue = auto.getOrThrow("auto") - - fun asManual(): Long = manual.getOrThrow("manual") - - fun _json(): Optional = Optional.ofNullable(_json) - - fun accept(visitor: Visitor): T = - when { - auto != null -> visitor.visitAuto(auto) - manual != null -> visitor.visitManual(manual) - else -> visitor.unknown(_json) - } - - private var validated: Boolean = false - - fun validate(): NEpochs = apply { - if (validated) { - return@apply - } - - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) { - auto.let { - if (it != JsonValue.from("auto")) { - throw OpenAIInvalidDataException( - "'auto' is invalid, received $it" - ) - } - } - } - - override fun visitManual(manual: Long) {} - } - ) - validated = true - } - - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitAuto(auto: JsonValue) = - auto.let { if (it == JsonValue.from("auto")) 1 else 0 } - - override fun visitManual(manual: Long) = 1 - - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is NEpochs && auto == other.auto && manual == other.manual /* spotless:on */ - } - - override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, manual) /* spotless:on */ - - override fun toString(): String = - when { - auto != null -> "NEpochs{auto=$auto}" - manual != null -> "NEpochs{manual=$manual}" - _json != null -> "NEpochs{_unknown=$_json}" - else -> throw IllegalStateException("Invalid NEpochs") - } - - companion object { - - @JvmStatic fun ofAuto() = NEpochs(auto = JsonValue.from("auto")) - - @JvmStatic fun ofManual(manual: Long) = NEpochs(manual = manual) - } - - /** - * An interface that defines how to map each variant of [NEpochs] to a value of - * type [T]. - */ - interface Visitor { - - fun visitAuto(auto: JsonValue): T - - fun visitManual(manual: Long): T - - /** - * Maps an unknown variant of [NEpochs] to a value of type [T]. - * - * An instance of [NEpochs] 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 NEpochs: $json") - } - } - - internal class Deserializer : BaseDeserializer(NEpochs::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): NEpochs { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { NEpochs(auto = it, _json = json) } - ?.takeIf { it.isValid() }, - tryDeserialize(node, jacksonTypeRef())?.let { - NEpochs(manual = it, _json = json) - }, - ) - .filterNotNull() - .allMaxBy { it.validity() } - .toList() - return when (bestMatches.size) { - // This can happen if what we're deserializing is completely - // incompatible with all the possible variants (e.g. deserializing - // from object). - 0 -> NEpochs(_json = json) - 1 -> bestMatches.single() - // If there's more than one match with the highest validity, then - // use the first completely valid match, or simply the first match - // if none are completely valid. - else -> - bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() - } - } - } - - internal class Serializer : BaseSerializer(NEpochs::class) { - - override fun serialize( - value: NEpochs, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.auto != null -> generator.writeObject(value.auto) - value.manual != null -> generator.writeObject(value.manual) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid NEpochs") - } - } - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is Hyperparameters && batchSize == other.batchSize && learningRateMultiplier == other.learningRateMultiplier && nEpochs == other.nEpochs && additionalProperties == other.additionalProperties /* spotless:on */ - } - - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(batchSize, learningRateMultiplier, nEpochs, additionalProperties) } - /* spotless:on */ - - override fun hashCode(): Int = hashCode - - override fun toString() = - "Hyperparameters{batchSize=$batchSize, learningRateMultiplier=$learningRateMultiplier, nEpochs=$nEpochs, additionalProperties=$additionalProperties}" - } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return /* spotless:off */ other is Supervised && hyperparameters == other.hyperparameters && additionalProperties == other.additionalProperties /* spotless:on */ - } - - /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(hyperparameters, additionalProperties) } - /* spotless:on */ - - override fun hashCode(): Int = hashCode - - override fun toString() = - "Supervised{hyperparameters=$hyperparameters, additionalProperties=$additionalProperties}" - } - - /** The type of method. Is either `supervised` or `dpo`. */ - class Type @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 SUPERVISED = of("supervised") - - @JvmField val DPO = of("dpo") + @JvmField val REINFORCEMENT = of("reinforcement") @JvmStatic fun of(value: String) = Type(JsonField.of(value)) } @@ -5290,6 +3123,7 @@ private constructor( enum class Known { SUPERVISED, DPO, + REINFORCEMENT, } /** @@ -5304,6 +3138,7 @@ private constructor( enum class Value { SUPERVISED, DPO, + REINFORCEMENT, /** An enum member indicating that [Type] was instantiated with an unknown value. */ _UNKNOWN, } @@ -5319,6 +3154,7 @@ private constructor( when (this) { SUPERVISED -> Value.SUPERVISED DPO -> Value.DPO + REINFORCEMENT -> Value.REINFORCEMENT else -> Value._UNKNOWN } @@ -5335,6 +3171,7 @@ private constructor( when (this) { SUPERVISED -> Known.SUPERVISED DPO -> Known.DPO + REINFORCEMENT -> Known.REINFORCEMENT else -> throw OpenAIInvalidDataException("Unknown Type: $value") } @@ -5397,17 +3234,17 @@ private constructor( return true } - return /* spotless:off */ other is Method && dpo == other.dpo && supervised == other.supervised && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is Method && type == other.type && dpo == other.dpo && reinforcement == other.reinforcement && supervised == other.supervised && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(dpo, supervised, type, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(type, dpo, reinforcement, supervised, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "Method{dpo=$dpo, supervised=$supervised, type=$type, additionalProperties=$additionalProperties}" + "Method{type=$type, dpo=$dpo, reinforcement=$reinforcement, supervised=$supervised, additionalProperties=$additionalProperties}" } override fun equals(other: Any?): Boolean { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPage.kt index 7acc932f..2a93a6c8 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPage.kt @@ -2,12 +2,12 @@ package com.openai.models.finetuning.jobs +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.finetuning.JobService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [JobService.listEvents] */ @@ -16,7 +16,7 @@ private constructor( private val service: JobService, private val params: JobListEventsParams, private val response: JobListEventsPageResponse, -) { +) : Page { /** * Delegates to [JobListEventsPageResponse], but gracefully handles missing data. @@ -33,20 +33,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): JobListEventsParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = - getNextPageParams().map { service.listEvents(it) } + override fun nextPage(): JobListEventsPage = service.listEvents(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): JobListEventsParams = params @@ -115,25 +111,6 @@ private constructor( ) } - class AutoPager(private val firstPage: JobListEventsPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPageAsync.kt index eceb1568..2dc4a3e2 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPageAsync.kt @@ -2,22 +2,24 @@ package com.openai.models.finetuning.jobs +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.finetuning.JobServiceAsync import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [JobServiceAsync.listEvents] */ class JobListEventsPageAsync private constructor( private val service: JobServiceAsync, + private val streamHandlerExecutor: Executor, private val params: JobListEventsParams, private val response: JobListEventsPageResponse, -) { +) : PageAsync { /** * Delegates to [JobListEventsPageResponse], but gracefully handles missing data. @@ -34,22 +36,18 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): JobListEventsParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.listEvents(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = + service.listEvents(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = + AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): JobListEventsParams = params @@ -67,6 +65,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -78,18 +77,24 @@ private constructor( class Builder internal constructor() { private var service: JobServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: JobListEventsParams? = null private var response: JobListEventsPageResponse? = null @JvmSynthetic internal fun from(jobListEventsPageAsync: JobListEventsPageAsync) = apply { service = jobListEventsPageAsync.service + streamHandlerExecutor = jobListEventsPageAsync.streamHandlerExecutor params = jobListEventsPageAsync.params response = jobListEventsPageAsync.response } fun service(service: JobServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: JobListEventsParams) = apply { this.params = params } @@ -104,6 +109,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -113,50 +119,22 @@ private constructor( fun build(): JobListEventsPageAsync = JobListEventsPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: JobListEventsPageAsync) { - - fun forEach( - action: Predicate, - executor: Executor, - ): CompletableFuture { - fun CompletableFuture>.forEach( - action: (FineTuningJobEvent) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is JobListEventsPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is JobListEventsPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "JobListEventsPageAsync{service=$service, params=$params, response=$response}" + "JobListEventsPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsParams.kt index 26348572..1922578d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsParams.kt @@ -3,7 +3,6 @@ package com.openai.models.finetuning.jobs import com.openai.core.Params -import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects @@ -13,14 +12,14 @@ import kotlin.jvm.optionals.getOrNull /** Get status updates for a fine-tuning job. */ class JobListEventsParams private constructor( - private val fineTuningJobId: String, + private val fineTuningJobId: String?, private val after: String?, private val limit: Long?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun fineTuningJobId(): String = fineTuningJobId + fun fineTuningJobId(): Optional = Optional.ofNullable(fineTuningJobId) /** Identifier for the last event from the previous pagination request. */ fun after(): Optional = Optional.ofNullable(after) @@ -36,14 +35,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [JobListEventsParams]. - * - * The following fields are required: - * ```java - * .fineTuningJobId() - * ``` - */ + @JvmStatic fun none(): JobListEventsParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [JobListEventsParams]. */ @JvmStatic fun builder() = Builder() } @@ -65,10 +59,14 @@ private constructor( additionalQueryParams = jobListEventsParams.additionalQueryParams.toBuilder() } - fun fineTuningJobId(fineTuningJobId: String) = apply { + fun fineTuningJobId(fineTuningJobId: String?) = apply { this.fineTuningJobId = fineTuningJobId } + /** Alias for calling [Builder.fineTuningJobId] with `fineTuningJobId.orElse(null)`. */ + fun fineTuningJobId(fineTuningJobId: Optional) = + fineTuningJobId(fineTuningJobId.getOrNull()) + /** Identifier for the last event from the previous pagination request. */ fun after(after: String?) = apply { this.after = after } @@ -190,17 +188,10 @@ private constructor( * Returns an immutable instance of [JobListEventsParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .fineTuningJobId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): JobListEventsParams = JobListEventsParams( - checkRequired("fineTuningJobId", fineTuningJobId), + fineTuningJobId, after, limit, additionalHeaders.build(), @@ -210,7 +201,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> fineTuningJobId + 0 -> fineTuningJobId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPage.kt index db6bcab0..7ed9ed47 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPage.kt @@ -2,12 +2,12 @@ package com.openai.models.finetuning.jobs +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.finetuning.JobService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [JobService.list] */ @@ -16,7 +16,7 @@ private constructor( private val service: JobService, private val params: JobListParams, private val response: JobListPageResponse, -) { +) : Page { /** * Delegates to [JobListPageResponse], but gracefully handles missing data. @@ -33,19 +33,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): JobListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + override fun nextPage(): JobListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): JobListParams = params @@ -114,25 +111,6 @@ private constructor( ) } - class AutoPager(private val firstPage: JobListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPageAsync.kt index 7730d220..5860505d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPageAsync.kt @@ -2,22 +2,24 @@ package com.openai.models.finetuning.jobs +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.finetuning.JobServiceAsync import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [JobServiceAsync.list] */ class JobListPageAsync private constructor( private val service: JobServiceAsync, + private val streamHandlerExecutor: Executor, private val params: JobListParams, private val response: JobListPageResponse, -) { +) : PageAsync { /** * Delegates to [JobListPageResponse], but gracefully handles missing data. @@ -34,22 +36,17 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): JobListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = + AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): JobListParams = params @@ -67,6 +64,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -78,18 +76,24 @@ private constructor( class Builder internal constructor() { private var service: JobServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: JobListParams? = null private var response: JobListPageResponse? = null @JvmSynthetic internal fun from(jobListPageAsync: JobListPageAsync) = apply { service = jobListPageAsync.service + streamHandlerExecutor = jobListPageAsync.streamHandlerExecutor params = jobListPageAsync.params response = jobListPageAsync.response } fun service(service: JobServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: JobListParams) = apply { this.params = params } @@ -104,6 +108,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -113,47 +118,22 @@ private constructor( fun build(): JobListPageAsync = JobListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: JobListPageAsync) { - - fun forEach(action: Predicate, executor: Executor): CompletableFuture { - fun CompletableFuture>.forEach( - action: (FineTuningJob) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is JobListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is JobListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "JobListPageAsync{service=$service, params=$params, response=$response}" + "JobListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobPauseParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobPauseParams.kt new file mode 100644 index 00000000..d4cc700b --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobPauseParams.kt @@ -0,0 +1,224 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.jobs + +import com.openai.core.JsonValue +import com.openai.core.Params +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 +import kotlin.jvm.optionals.getOrNull + +/** Pause a fine-tune job. */ +class JobPauseParams +private constructor( + private val fineTuningJobId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, + private val additionalBodyProperties: Map, +) : Params { + + fun fineTuningJobId(): Optional = Optional.ofNullable(fineTuningJobId) + + fun _additionalBodyProperties(): Map = additionalBodyProperties + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun none(): JobPauseParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [JobPauseParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [JobPauseParams]. */ + class Builder internal constructor() { + + private var fineTuningJobId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + private var additionalBodyProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(jobPauseParams: JobPauseParams) = apply { + fineTuningJobId = jobPauseParams.fineTuningJobId + additionalHeaders = jobPauseParams.additionalHeaders.toBuilder() + additionalQueryParams = jobPauseParams.additionalQueryParams.toBuilder() + additionalBodyProperties = jobPauseParams.additionalBodyProperties.toMutableMap() + } + + fun fineTuningJobId(fineTuningJobId: String?) = apply { + this.fineTuningJobId = fineTuningJobId + } + + /** Alias for calling [Builder.fineTuningJobId] with `fineTuningJobId.orElse(null)`. */ + fun fineTuningJobId(fineTuningJobId: Optional) = + fineTuningJobId(fineTuningJobId.getOrNull()) + + 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 additionalBodyProperties(additionalBodyProperties: Map) = apply { + this.additionalBodyProperties.clear() + putAllAdditionalBodyProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + additionalBodyProperties.put(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + this.additionalBodyProperties.putAll(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { + additionalBodyProperties.remove(key) + } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalBodyProperty) + } + + /** + * Returns an immutable instance of [JobPauseParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): JobPauseParams = + JobPauseParams( + fineTuningJobId, + additionalHeaders.build(), + additionalQueryParams.build(), + additionalBodyProperties.toImmutable(), + ) + } + + fun _body(): Optional> = + Optional.ofNullable(additionalBodyProperties.ifEmpty { null }) + + fun _pathParam(index: Int): String = + when (index) { + 0 -> fineTuningJobId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is JobPauseParams && fineTuningJobId == other.fineTuningJobId && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams && additionalBodyProperties == other.additionalBodyProperties /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(fineTuningJobId, additionalHeaders, additionalQueryParams, additionalBodyProperties) /* spotless:on */ + + override fun toString() = + "JobPauseParams{fineTuningJobId=$fineTuningJobId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobResumeParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobResumeParams.kt new file mode 100644 index 00000000..97f214e3 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobResumeParams.kt @@ -0,0 +1,224 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.jobs + +import com.openai.core.JsonValue +import com.openai.core.Params +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 +import kotlin.jvm.optionals.getOrNull + +/** Resume a fine-tune job. */ +class JobResumeParams +private constructor( + private val fineTuningJobId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, + private val additionalBodyProperties: Map, +) : Params { + + fun fineTuningJobId(): Optional = Optional.ofNullable(fineTuningJobId) + + fun _additionalBodyProperties(): Map = additionalBodyProperties + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun none(): JobResumeParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [JobResumeParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [JobResumeParams]. */ + class Builder internal constructor() { + + private var fineTuningJobId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + private var additionalBodyProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(jobResumeParams: JobResumeParams) = apply { + fineTuningJobId = jobResumeParams.fineTuningJobId + additionalHeaders = jobResumeParams.additionalHeaders.toBuilder() + additionalQueryParams = jobResumeParams.additionalQueryParams.toBuilder() + additionalBodyProperties = jobResumeParams.additionalBodyProperties.toMutableMap() + } + + fun fineTuningJobId(fineTuningJobId: String?) = apply { + this.fineTuningJobId = fineTuningJobId + } + + /** Alias for calling [Builder.fineTuningJobId] with `fineTuningJobId.orElse(null)`. */ + fun fineTuningJobId(fineTuningJobId: Optional) = + fineTuningJobId(fineTuningJobId.getOrNull()) + + 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 additionalBodyProperties(additionalBodyProperties: Map) = apply { + this.additionalBodyProperties.clear() + putAllAdditionalBodyProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + additionalBodyProperties.put(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + this.additionalBodyProperties.putAll(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { + additionalBodyProperties.remove(key) + } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalBodyProperty) + } + + /** + * Returns an immutable instance of [JobResumeParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): JobResumeParams = + JobResumeParams( + fineTuningJobId, + additionalHeaders.build(), + additionalQueryParams.build(), + additionalBodyProperties.toImmutable(), + ) + } + + fun _body(): Optional> = + Optional.ofNullable(additionalBodyProperties.ifEmpty { null }) + + fun _pathParam(index: Int): String = + when (index) { + 0 -> fineTuningJobId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is JobResumeParams && fineTuningJobId == other.fineTuningJobId && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams && additionalBodyProperties == other.additionalBodyProperties /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(fineTuningJobId, additionalHeaders, additionalQueryParams, additionalBodyProperties) /* spotless:on */ + + override fun toString() = + "JobResumeParams{fineTuningJobId=$fineTuningJobId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobRetrieveParams.kt index 2e5e2874..3a5e0814 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobRetrieveParams.kt @@ -3,10 +3,11 @@ package com.openai.models.finetuning.jobs import com.openai.core.Params -import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Get info about a fine-tuning job. @@ -15,12 +16,12 @@ import java.util.Objects */ class JobRetrieveParams private constructor( - private val fineTuningJobId: String, + private val fineTuningJobId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun fineTuningJobId(): String = fineTuningJobId + fun fineTuningJobId(): Optional = Optional.ofNullable(fineTuningJobId) fun _additionalHeaders(): Headers = additionalHeaders @@ -30,14 +31,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [JobRetrieveParams]. - * - * The following fields are required: - * ```java - * .fineTuningJobId() - * ``` - */ + @JvmStatic fun none(): JobRetrieveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [JobRetrieveParams]. */ @JvmStatic fun builder() = Builder() } @@ -55,10 +51,14 @@ private constructor( additionalQueryParams = jobRetrieveParams.additionalQueryParams.toBuilder() } - fun fineTuningJobId(fineTuningJobId: String) = apply { + fun fineTuningJobId(fineTuningJobId: String?) = apply { this.fineTuningJobId = fineTuningJobId } + /** Alias for calling [Builder.fineTuningJobId] with `fineTuningJobId.orElse(null)`. */ + fun fineTuningJobId(fineTuningJobId: Optional) = + fineTuningJobId(fineTuningJobId.getOrNull()) + fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() putAllAdditionalHeaders(additionalHeaders) @@ -161,17 +161,10 @@ private constructor( * Returns an immutable instance of [JobRetrieveParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .fineTuningJobId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): JobRetrieveParams = JobRetrieveParams( - checkRequired("fineTuningJobId", fineTuningJobId), + fineTuningJobId, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -179,7 +172,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> fineTuningJobId + 0 -> fineTuningJobId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPage.kt index 64c6ce8b..e5df9890 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPage.kt @@ -2,12 +2,12 @@ package com.openai.models.finetuning.jobs.checkpoints +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.finetuning.jobs.CheckpointService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [CheckpointService.list] */ @@ -16,7 +16,7 @@ private constructor( private val service: CheckpointService, private val params: CheckpointListParams, private val response: CheckpointListPageResponse, -) { +) : Page { /** * Delegates to [CheckpointListPageResponse], but gracefully handles missing data. @@ -33,19 +33,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): CheckpointListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + override fun nextPage(): CheckpointListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): CheckpointListParams = params @@ -114,25 +111,6 @@ private constructor( ) } - class AutoPager(private val firstPage: CheckpointListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPageAsync.kt index b5812ee7..4c377a56 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPageAsync.kt @@ -2,22 +2,24 @@ package com.openai.models.finetuning.jobs.checkpoints +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.finetuning.jobs.CheckpointServiceAsync import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [CheckpointServiceAsync.list] */ class CheckpointListPageAsync private constructor( private val service: CheckpointServiceAsync, + private val streamHandlerExecutor: Executor, private val params: CheckpointListParams, private val response: CheckpointListPageResponse, -) { +) : PageAsync { /** * Delegates to [CheckpointListPageResponse], but gracefully handles missing data. @@ -34,22 +36,18 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): CheckpointListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = + service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = + AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): CheckpointListParams = params @@ -67,6 +65,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -78,18 +77,24 @@ private constructor( class Builder internal constructor() { private var service: CheckpointServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: CheckpointListParams? = null private var response: CheckpointListPageResponse? = null @JvmSynthetic internal fun from(checkpointListPageAsync: CheckpointListPageAsync) = apply { service = checkpointListPageAsync.service + streamHandlerExecutor = checkpointListPageAsync.streamHandlerExecutor params = checkpointListPageAsync.params response = checkpointListPageAsync.response } fun service(service: CheckpointServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: CheckpointListParams) = apply { this.params = params } @@ -104,6 +109,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -113,50 +119,22 @@ private constructor( fun build(): CheckpointListPageAsync = CheckpointListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: CheckpointListPageAsync) { - - fun forEach( - action: Predicate, - executor: Executor, - ): CompletableFuture { - fun CompletableFuture>.forEach( - action: (FineTuningJobCheckpoint) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is CheckpointListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is CheckpointListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "CheckpointListPageAsync{service=$service, params=$params, response=$response}" + "CheckpointListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListParams.kt index 917d0458..46483d0c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListParams.kt @@ -3,7 +3,6 @@ package com.openai.models.finetuning.jobs.checkpoints import com.openai.core.Params -import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects @@ -13,14 +12,14 @@ import kotlin.jvm.optionals.getOrNull /** List checkpoints for a fine-tuning job. */ class CheckpointListParams private constructor( - private val fineTuningJobId: String, + private val fineTuningJobId: String?, private val after: String?, private val limit: Long?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun fineTuningJobId(): String = fineTuningJobId + fun fineTuningJobId(): Optional = Optional.ofNullable(fineTuningJobId) /** Identifier for the last checkpoint ID from the previous pagination request. */ fun after(): Optional = Optional.ofNullable(after) @@ -36,14 +35,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [CheckpointListParams]. - * - * The following fields are required: - * ```java - * .fineTuningJobId() - * ``` - */ + @JvmStatic fun none(): CheckpointListParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [CheckpointListParams]. */ @JvmStatic fun builder() = Builder() } @@ -65,10 +59,14 @@ private constructor( additionalQueryParams = checkpointListParams.additionalQueryParams.toBuilder() } - fun fineTuningJobId(fineTuningJobId: String) = apply { + fun fineTuningJobId(fineTuningJobId: String?) = apply { this.fineTuningJobId = fineTuningJobId } + /** Alias for calling [Builder.fineTuningJobId] with `fineTuningJobId.orElse(null)`. */ + fun fineTuningJobId(fineTuningJobId: Optional) = + fineTuningJobId(fineTuningJobId.getOrNull()) + /** Identifier for the last checkpoint ID from the previous pagination request. */ fun after(after: String?) = apply { this.after = after } @@ -190,17 +188,10 @@ private constructor( * Returns an immutable instance of [CheckpointListParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .fineTuningJobId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): CheckpointListParams = CheckpointListParams( - checkRequired("fineTuningJobId", fineTuningJobId), + fineTuningJobId, after, limit, additionalHeaders.build(), @@ -210,7 +201,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> fineTuningJobId + 0 -> fineTuningJobId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/DpoHyperparameters.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/DpoHyperparameters.kt new file mode 100644 index 00000000..e3c8b5c3 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/DpoHyperparameters.kt @@ -0,0 +1,1050 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.methods + +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.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.ExcludeMissing +import com.openai.core.JsonField +import com.openai.core.JsonMissing +import com.openai.core.JsonValue +import com.openai.core.allMaxBy +import com.openai.core.getOrThrow +import com.openai.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** The hyperparameters used for the DPO fine-tuning job. */ +class DpoHyperparameters +private constructor( + private val batchSize: JsonField, + private val beta: JsonField, + private val learningRateMultiplier: JsonField, + private val nEpochs: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("batch_size") + @ExcludeMissing + batchSize: JsonField = JsonMissing.of(), + @JsonProperty("beta") @ExcludeMissing beta: JsonField = JsonMissing.of(), + @JsonProperty("learning_rate_multiplier") + @ExcludeMissing + learningRateMultiplier: JsonField = JsonMissing.of(), + @JsonProperty("n_epochs") @ExcludeMissing nEpochs: JsonField = JsonMissing.of(), + ) : this(batchSize, beta, learningRateMultiplier, nEpochs, mutableMapOf()) + + /** + * Number of examples in each batch. A larger batch size means that model parameters are updated + * less frequently, but with lower variance. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun batchSize(): Optional = batchSize.getOptional("batch_size") + + /** + * The beta value for the DPO method. A higher beta value will increase the weight of the + * penalty between the policy and reference model. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun beta(): Optional = beta.getOptional("beta") + + /** + * Scaling factor for the learning rate. A smaller learning rate may be useful to avoid + * overfitting. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun learningRateMultiplier(): Optional = + learningRateMultiplier.getOptional("learning_rate_multiplier") + + /** + * The number of epochs to train the model for. An epoch refers to one full cycle through the + * training dataset. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun nEpochs(): Optional = nEpochs.getOptional("n_epochs") + + /** + * Returns the raw JSON value of [batchSize]. + * + * Unlike [batchSize], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("batch_size") @ExcludeMissing fun _batchSize(): JsonField = batchSize + + /** + * Returns the raw JSON value of [beta]. + * + * Unlike [beta], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("beta") @ExcludeMissing fun _beta(): JsonField = beta + + /** + * Returns the raw JSON value of [learningRateMultiplier]. + * + * Unlike [learningRateMultiplier], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("learning_rate_multiplier") + @ExcludeMissing + fun _learningRateMultiplier(): JsonField = learningRateMultiplier + + /** + * Returns the raw JSON value of [nEpochs]. + * + * Unlike [nEpochs], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("n_epochs") @ExcludeMissing fun _nEpochs(): JsonField = nEpochs + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [DpoHyperparameters]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [DpoHyperparameters]. */ + class Builder internal constructor() { + + private var batchSize: JsonField = JsonMissing.of() + private var beta: JsonField = JsonMissing.of() + private var learningRateMultiplier: JsonField = JsonMissing.of() + private var nEpochs: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(dpoHyperparameters: DpoHyperparameters) = apply { + batchSize = dpoHyperparameters.batchSize + beta = dpoHyperparameters.beta + learningRateMultiplier = dpoHyperparameters.learningRateMultiplier + nEpochs = dpoHyperparameters.nEpochs + additionalProperties = dpoHyperparameters.additionalProperties.toMutableMap() + } + + /** + * Number of examples in each batch. A larger batch size means that model parameters are + * updated less frequently, but with lower variance. + */ + fun batchSize(batchSize: BatchSize) = batchSize(JsonField.of(batchSize)) + + /** + * Sets [Builder.batchSize] to an arbitrary JSON value. + * + * You should usually call [Builder.batchSize] with a well-typed [BatchSize] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun batchSize(batchSize: JsonField) = apply { this.batchSize = batchSize } + + /** Alias for calling [batchSize] with `BatchSize.ofAuto()`. */ + fun batchSizeAuto() = batchSize(BatchSize.ofAuto()) + + /** Alias for calling [batchSize] with `BatchSize.ofInteger(integer)`. */ + fun batchSize(integer: Long) = batchSize(BatchSize.ofInteger(integer)) + + /** + * The beta value for the DPO method. A higher beta value will increase the weight of the + * penalty between the policy and reference model. + */ + fun beta(beta: Beta) = beta(JsonField.of(beta)) + + /** + * Sets [Builder.beta] to an arbitrary JSON value. + * + * You should usually call [Builder.beta] with a well-typed [Beta] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun beta(beta: JsonField) = apply { this.beta = beta } + + /** Alias for calling [beta] with `Beta.ofAuto()`. */ + fun betaAuto() = beta(Beta.ofAuto()) + + /** Alias for calling [beta] with `Beta.ofNumber(number)`. */ + fun beta(number: Double) = beta(Beta.ofNumber(number)) + + /** + * Scaling factor for the learning rate. A smaller learning rate may be useful to avoid + * overfitting. + */ + fun learningRateMultiplier(learningRateMultiplier: LearningRateMultiplier) = + learningRateMultiplier(JsonField.of(learningRateMultiplier)) + + /** + * Sets [Builder.learningRateMultiplier] to an arbitrary JSON value. + * + * You should usually call [Builder.learningRateMultiplier] with a well-typed + * [LearningRateMultiplier] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun learningRateMultiplier(learningRateMultiplier: JsonField) = + apply { + this.learningRateMultiplier = learningRateMultiplier + } + + /** Alias for calling [learningRateMultiplier] with `LearningRateMultiplier.ofAuto()`. */ + fun learningRateMultiplierAuto() = learningRateMultiplier(LearningRateMultiplier.ofAuto()) + + /** + * Alias for calling [learningRateMultiplier] with + * `LearningRateMultiplier.ofNumber(number)`. + */ + fun learningRateMultiplier(number: Double) = + learningRateMultiplier(LearningRateMultiplier.ofNumber(number)) + + /** + * The number of epochs to train the model for. An epoch refers to one full cycle through + * the training dataset. + */ + fun nEpochs(nEpochs: NEpochs) = nEpochs(JsonField.of(nEpochs)) + + /** + * Sets [Builder.nEpochs] to an arbitrary JSON value. + * + * You should usually call [Builder.nEpochs] with a well-typed [NEpochs] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun nEpochs(nEpochs: JsonField) = apply { this.nEpochs = nEpochs } + + /** Alias for calling [nEpochs] with `NEpochs.ofAuto()`. */ + fun nEpochsAuto() = nEpochs(NEpochs.ofAuto()) + + /** Alias for calling [nEpochs] with `NEpochs.ofInteger(integer)`. */ + fun nEpochs(integer: Long) = nEpochs(NEpochs.ofInteger(integer)) + + 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) + } + + /** + * Returns an immutable instance of [DpoHyperparameters]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): DpoHyperparameters = + DpoHyperparameters( + batchSize, + beta, + learningRateMultiplier, + nEpochs, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): DpoHyperparameters = apply { + if (validated) { + return@apply + } + + batchSize().ifPresent { it.validate() } + beta().ifPresent { it.validate() } + learningRateMultiplier().ifPresent { it.validate() } + nEpochs().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (batchSize.asKnown().getOrNull()?.validity() ?: 0) + + (beta.asKnown().getOrNull()?.validity() ?: 0) + + (learningRateMultiplier.asKnown().getOrNull()?.validity() ?: 0) + + (nEpochs.asKnown().getOrNull()?.validity() ?: 0) + + /** + * Number of examples in each batch. A larger batch size means that model parameters are updated + * less frequently, but with lower variance. + */ + @JsonDeserialize(using = BatchSize.Deserializer::class) + @JsonSerialize(using = BatchSize.Serializer::class) + class BatchSize + private constructor( + private val auto: JsonValue? = null, + private val integer: Long? = null, + private val _json: JsonValue? = null, + ) { + + fun auto(): Optional = Optional.ofNullable(auto) + + fun integer(): Optional = Optional.ofNullable(integer) + + fun isAuto(): Boolean = auto != null + + fun isInteger(): Boolean = integer != null + + fun asAuto(): JsonValue = auto.getOrThrow("auto") + + fun asInteger(): Long = integer.getOrThrow("integer") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + auto != null -> visitor.visitAuto(auto) + integer != null -> visitor.visitInteger(integer) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): BatchSize = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) { + auto.let { + if (it != JsonValue.from("auto")) { + throw OpenAIInvalidDataException("'auto' is invalid, received $it") + } + } + } + + override fun visitInteger(integer: Long) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitInteger(integer: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is BatchSize && auto == other.auto && integer == other.integer /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, integer) /* spotless:on */ + + override fun toString(): String = + when { + auto != null -> "BatchSize{auto=$auto}" + integer != null -> "BatchSize{integer=$integer}" + _json != null -> "BatchSize{_unknown=$_json}" + else -> throw IllegalStateException("Invalid BatchSize") + } + + companion object { + + @JvmStatic fun ofAuto() = BatchSize(auto = JsonValue.from("auto")) + + @JvmStatic fun ofInteger(integer: Long) = BatchSize(integer = integer) + } + + /** + * An interface that defines how to map each variant of [BatchSize] to a value of type [T]. + */ + interface Visitor { + + fun visitAuto(auto: JsonValue): T + + fun visitInteger(integer: Long): T + + /** + * Maps an unknown variant of [BatchSize] to a value of type [T]. + * + * An instance of [BatchSize] 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 BatchSize: $json") + } + } + + internal class Deserializer : BaseDeserializer(BatchSize::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): BatchSize { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { BatchSize(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + BatchSize(integer = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from object). + 0 -> BatchSize(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(BatchSize::class) { + + override fun serialize( + value: BatchSize, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.auto != null -> generator.writeObject(value.auto) + value.integer != null -> generator.writeObject(value.integer) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid BatchSize") + } + } + } + } + + /** + * The beta value for the DPO method. A higher beta value will increase the weight of the + * penalty between the policy and reference model. + */ + @JsonDeserialize(using = Beta.Deserializer::class) + @JsonSerialize(using = Beta.Serializer::class) + class Beta + private constructor( + private val auto: JsonValue? = null, + private val number: Double? = null, + private val _json: JsonValue? = null, + ) { + + fun auto(): Optional = Optional.ofNullable(auto) + + fun number(): Optional = Optional.ofNullable(number) + + fun isAuto(): Boolean = auto != null + + fun isNumber(): Boolean = number != null + + fun asAuto(): JsonValue = auto.getOrThrow("auto") + + fun asNumber(): Double = number.getOrThrow("number") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + auto != null -> visitor.visitAuto(auto) + number != null -> visitor.visitNumber(number) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Beta = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) { + auto.let { + if (it != JsonValue.from("auto")) { + throw OpenAIInvalidDataException("'auto' is invalid, received $it") + } + } + } + + override fun visitNumber(number: Double) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitNumber(number: Double) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Beta && auto == other.auto && number == other.number /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, number) /* spotless:on */ + + override fun toString(): String = + when { + auto != null -> "Beta{auto=$auto}" + number != null -> "Beta{number=$number}" + _json != null -> "Beta{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Beta") + } + + companion object { + + @JvmStatic fun ofAuto() = Beta(auto = JsonValue.from("auto")) + + @JvmStatic fun ofNumber(number: Double) = Beta(number = number) + } + + /** An interface that defines how to map each variant of [Beta] to a value of type [T]. */ + interface Visitor { + + fun visitAuto(auto: JsonValue): T + + fun visitNumber(number: Double): T + + /** + * Maps an unknown variant of [Beta] to a value of type [T]. + * + * An instance of [Beta] 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 Beta: $json") + } + } + + internal class Deserializer : BaseDeserializer(Beta::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Beta { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { Beta(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + Beta(number = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from object). + 0 -> Beta(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Beta::class) { + + override fun serialize( + value: Beta, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.auto != null -> generator.writeObject(value.auto) + value.number != null -> generator.writeObject(value.number) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Beta") + } + } + } + } + + /** + * Scaling factor for the learning rate. A smaller learning rate may be useful to avoid + * overfitting. + */ + @JsonDeserialize(using = LearningRateMultiplier.Deserializer::class) + @JsonSerialize(using = LearningRateMultiplier.Serializer::class) + class LearningRateMultiplier + private constructor( + private val auto: JsonValue? = null, + private val number: Double? = null, + private val _json: JsonValue? = null, + ) { + + fun auto(): Optional = Optional.ofNullable(auto) + + fun number(): Optional = Optional.ofNullable(number) + + fun isAuto(): Boolean = auto != null + + fun isNumber(): Boolean = number != null + + fun asAuto(): JsonValue = auto.getOrThrow("auto") + + fun asNumber(): Double = number.getOrThrow("number") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + auto != null -> visitor.visitAuto(auto) + number != null -> visitor.visitNumber(number) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): LearningRateMultiplier = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) { + auto.let { + if (it != JsonValue.from("auto")) { + throw OpenAIInvalidDataException("'auto' is invalid, received $it") + } + } + } + + override fun visitNumber(number: Double) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitNumber(number: Double) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is LearningRateMultiplier && auto == other.auto && number == other.number /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, number) /* spotless:on */ + + override fun toString(): String = + when { + auto != null -> "LearningRateMultiplier{auto=$auto}" + number != null -> "LearningRateMultiplier{number=$number}" + _json != null -> "LearningRateMultiplier{_unknown=$_json}" + else -> throw IllegalStateException("Invalid LearningRateMultiplier") + } + + companion object { + + @JvmStatic fun ofAuto() = LearningRateMultiplier(auto = JsonValue.from("auto")) + + @JvmStatic fun ofNumber(number: Double) = LearningRateMultiplier(number = number) + } + + /** + * An interface that defines how to map each variant of [LearningRateMultiplier] to a value + * of type [T]. + */ + interface Visitor { + + fun visitAuto(auto: JsonValue): T + + fun visitNumber(number: Double): T + + /** + * Maps an unknown variant of [LearningRateMultiplier] to a value of type [T]. + * + * An instance of [LearningRateMultiplier] 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 LearningRateMultiplier: $json") + } + } + + internal class Deserializer : + BaseDeserializer(LearningRateMultiplier::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): LearningRateMultiplier { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { LearningRateMultiplier(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + LearningRateMultiplier(number = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from object). + 0 -> LearningRateMultiplier(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(LearningRateMultiplier::class) { + + override fun serialize( + value: LearningRateMultiplier, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.auto != null -> generator.writeObject(value.auto) + value.number != null -> generator.writeObject(value.number) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid LearningRateMultiplier") + } + } + } + } + + /** + * The number of epochs to train the model for. An epoch refers to one full cycle through the + * training dataset. + */ + @JsonDeserialize(using = NEpochs.Deserializer::class) + @JsonSerialize(using = NEpochs.Serializer::class) + class NEpochs + private constructor( + private val auto: JsonValue? = null, + private val integer: Long? = null, + private val _json: JsonValue? = null, + ) { + + fun auto(): Optional = Optional.ofNullable(auto) + + fun integer(): Optional = Optional.ofNullable(integer) + + fun isAuto(): Boolean = auto != null + + fun isInteger(): Boolean = integer != null + + fun asAuto(): JsonValue = auto.getOrThrow("auto") + + fun asInteger(): Long = integer.getOrThrow("integer") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + auto != null -> visitor.visitAuto(auto) + integer != null -> visitor.visitInteger(integer) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): NEpochs = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) { + auto.let { + if (it != JsonValue.from("auto")) { + throw OpenAIInvalidDataException("'auto' is invalid, received $it") + } + } + } + + override fun visitInteger(integer: Long) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitInteger(integer: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is NEpochs && auto == other.auto && integer == other.integer /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, integer) /* spotless:on */ + + override fun toString(): String = + when { + auto != null -> "NEpochs{auto=$auto}" + integer != null -> "NEpochs{integer=$integer}" + _json != null -> "NEpochs{_unknown=$_json}" + else -> throw IllegalStateException("Invalid NEpochs") + } + + companion object { + + @JvmStatic fun ofAuto() = NEpochs(auto = JsonValue.from("auto")) + + @JvmStatic fun ofInteger(integer: Long) = NEpochs(integer = integer) + } + + /** + * An interface that defines how to map each variant of [NEpochs] to a value of type [T]. + */ + interface Visitor { + + fun visitAuto(auto: JsonValue): T + + fun visitInteger(integer: Long): T + + /** + * Maps an unknown variant of [NEpochs] to a value of type [T]. + * + * An instance of [NEpochs] 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 NEpochs: $json") + } + } + + internal class Deserializer : BaseDeserializer(NEpochs::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): NEpochs { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { NEpochs(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + NEpochs(integer = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from object). + 0 -> NEpochs(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(NEpochs::class) { + + override fun serialize( + value: NEpochs, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.auto != null -> generator.writeObject(value.auto) + value.integer != null -> generator.writeObject(value.integer) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid NEpochs") + } + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is DpoHyperparameters && batchSize == other.batchSize && beta == other.beta && learningRateMultiplier == other.learningRateMultiplier && nEpochs == other.nEpochs && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(batchSize, beta, learningRateMultiplier, nEpochs, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "DpoHyperparameters{batchSize=$batchSize, beta=$beta, learningRateMultiplier=$learningRateMultiplier, nEpochs=$nEpochs, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/DpoMethod.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/DpoMethod.kt new file mode 100644 index 00000000..bc489634 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/DpoMethod.kt @@ -0,0 +1,166 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.methods + +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.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** Configuration for the DPO fine-tuning method. */ +class DpoMethod +private constructor( + private val hyperparameters: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("hyperparameters") + @ExcludeMissing + hyperparameters: JsonField = JsonMissing.of() + ) : this(hyperparameters, mutableMapOf()) + + /** + * The hyperparameters used for the DPO fine-tuning job. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun hyperparameters(): Optional = + hyperparameters.getOptional("hyperparameters") + + /** + * Returns the raw JSON value of [hyperparameters]. + * + * Unlike [hyperparameters], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hyperparameters") + @ExcludeMissing + fun _hyperparameters(): JsonField = hyperparameters + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [DpoMethod]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [DpoMethod]. */ + class Builder internal constructor() { + + private var hyperparameters: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(dpoMethod: DpoMethod) = apply { + hyperparameters = dpoMethod.hyperparameters + additionalProperties = dpoMethod.additionalProperties.toMutableMap() + } + + /** The hyperparameters used for the DPO fine-tuning job. */ + fun hyperparameters(hyperparameters: DpoHyperparameters) = + hyperparameters(JsonField.of(hyperparameters)) + + /** + * Sets [Builder.hyperparameters] to an arbitrary JSON value. + * + * You should usually call [Builder.hyperparameters] with a well-typed [DpoHyperparameters] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun hyperparameters(hyperparameters: JsonField) = apply { + this.hyperparameters = hyperparameters + } + + 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) + } + + /** + * Returns an immutable instance of [DpoMethod]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): DpoMethod = DpoMethod(hyperparameters, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): DpoMethod = apply { + if (validated) { + return@apply + } + + hyperparameters().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = (hyperparameters.asKnown().getOrNull()?.validity() ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is DpoMethod && hyperparameters == other.hyperparameters && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(hyperparameters, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "DpoMethod{hyperparameters=$hyperparameters, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/ReinforcementHyperparameters.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/ReinforcementHyperparameters.kt new file mode 100644 index 00000000..99840ec7 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/ReinforcementHyperparameters.kt @@ -0,0 +1,1703 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.methods + +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.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.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.allMaxBy +import com.openai.core.getOrThrow +import com.openai.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** The hyperparameters used for the reinforcement fine-tuning job. */ +class ReinforcementHyperparameters +private constructor( + private val batchSize: JsonField, + private val computeMultiplier: JsonField, + private val evalInterval: JsonField, + private val evalSamples: JsonField, + private val learningRateMultiplier: JsonField, + private val nEpochs: JsonField, + private val reasoningEffort: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("batch_size") + @ExcludeMissing + batchSize: JsonField = JsonMissing.of(), + @JsonProperty("compute_multiplier") + @ExcludeMissing + computeMultiplier: JsonField = JsonMissing.of(), + @JsonProperty("eval_interval") + @ExcludeMissing + evalInterval: JsonField = JsonMissing.of(), + @JsonProperty("eval_samples") + @ExcludeMissing + evalSamples: JsonField = JsonMissing.of(), + @JsonProperty("learning_rate_multiplier") + @ExcludeMissing + learningRateMultiplier: JsonField = JsonMissing.of(), + @JsonProperty("n_epochs") @ExcludeMissing nEpochs: JsonField = JsonMissing.of(), + @JsonProperty("reasoning_effort") + @ExcludeMissing + reasoningEffort: JsonField = JsonMissing.of(), + ) : this( + batchSize, + computeMultiplier, + evalInterval, + evalSamples, + learningRateMultiplier, + nEpochs, + reasoningEffort, + mutableMapOf(), + ) + + /** + * Number of examples in each batch. A larger batch size means that model parameters are updated + * less frequently, but with lower variance. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun batchSize(): Optional = batchSize.getOptional("batch_size") + + /** + * Multiplier on amount of compute used for exploring search space during training. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun computeMultiplier(): Optional = + computeMultiplier.getOptional("compute_multiplier") + + /** + * The number of training steps between evaluation runs. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun evalInterval(): Optional = evalInterval.getOptional("eval_interval") + + /** + * Number of evaluation samples to generate per training step. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun evalSamples(): Optional = evalSamples.getOptional("eval_samples") + + /** + * Scaling factor for the learning rate. A smaller learning rate may be useful to avoid + * overfitting. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun learningRateMultiplier(): Optional = + learningRateMultiplier.getOptional("learning_rate_multiplier") + + /** + * The number of epochs to train the model for. An epoch refers to one full cycle through the + * training dataset. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun nEpochs(): Optional = nEpochs.getOptional("n_epochs") + + /** + * Level of reasoning effort. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun reasoningEffort(): Optional = + reasoningEffort.getOptional("reasoning_effort") + + /** + * Returns the raw JSON value of [batchSize]. + * + * Unlike [batchSize], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("batch_size") @ExcludeMissing fun _batchSize(): JsonField = batchSize + + /** + * Returns the raw JSON value of [computeMultiplier]. + * + * Unlike [computeMultiplier], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("compute_multiplier") + @ExcludeMissing + fun _computeMultiplier(): JsonField = computeMultiplier + + /** + * Returns the raw JSON value of [evalInterval]. + * + * Unlike [evalInterval], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("eval_interval") + @ExcludeMissing + fun _evalInterval(): JsonField = evalInterval + + /** + * Returns the raw JSON value of [evalSamples]. + * + * Unlike [evalSamples], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("eval_samples") + @ExcludeMissing + fun _evalSamples(): JsonField = evalSamples + + /** + * Returns the raw JSON value of [learningRateMultiplier]. + * + * Unlike [learningRateMultiplier], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("learning_rate_multiplier") + @ExcludeMissing + fun _learningRateMultiplier(): JsonField = learningRateMultiplier + + /** + * Returns the raw JSON value of [nEpochs]. + * + * Unlike [nEpochs], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("n_epochs") @ExcludeMissing fun _nEpochs(): JsonField = nEpochs + + /** + * Returns the raw JSON value of [reasoningEffort]. + * + * Unlike [reasoningEffort], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("reasoning_effort") + @ExcludeMissing + fun _reasoningEffort(): JsonField = reasoningEffort + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ReinforcementHyperparameters]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ReinforcementHyperparameters]. */ + class Builder internal constructor() { + + private var batchSize: JsonField = JsonMissing.of() + private var computeMultiplier: JsonField = JsonMissing.of() + private var evalInterval: JsonField = JsonMissing.of() + private var evalSamples: JsonField = JsonMissing.of() + private var learningRateMultiplier: JsonField = JsonMissing.of() + private var nEpochs: JsonField = JsonMissing.of() + private var reasoningEffort: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(reinforcementHyperparameters: ReinforcementHyperparameters) = apply { + batchSize = reinforcementHyperparameters.batchSize + computeMultiplier = reinforcementHyperparameters.computeMultiplier + evalInterval = reinforcementHyperparameters.evalInterval + evalSamples = reinforcementHyperparameters.evalSamples + learningRateMultiplier = reinforcementHyperparameters.learningRateMultiplier + nEpochs = reinforcementHyperparameters.nEpochs + reasoningEffort = reinforcementHyperparameters.reasoningEffort + additionalProperties = reinforcementHyperparameters.additionalProperties.toMutableMap() + } + + /** + * Number of examples in each batch. A larger batch size means that model parameters are + * updated less frequently, but with lower variance. + */ + fun batchSize(batchSize: BatchSize) = batchSize(JsonField.of(batchSize)) + + /** + * Sets [Builder.batchSize] to an arbitrary JSON value. + * + * You should usually call [Builder.batchSize] with a well-typed [BatchSize] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun batchSize(batchSize: JsonField) = apply { this.batchSize = batchSize } + + /** Alias for calling [batchSize] with `BatchSize.ofAuto()`. */ + fun batchSizeAuto() = batchSize(BatchSize.ofAuto()) + + /** Alias for calling [batchSize] with `BatchSize.ofInteger(integer)`. */ + fun batchSize(integer: Long) = batchSize(BatchSize.ofInteger(integer)) + + /** Multiplier on amount of compute used for exploring search space during training. */ + fun computeMultiplier(computeMultiplier: ComputeMultiplier) = + computeMultiplier(JsonField.of(computeMultiplier)) + + /** + * Sets [Builder.computeMultiplier] to an arbitrary JSON value. + * + * You should usually call [Builder.computeMultiplier] with a well-typed [ComputeMultiplier] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun computeMultiplier(computeMultiplier: JsonField) = apply { + this.computeMultiplier = computeMultiplier + } + + /** Alias for calling [computeMultiplier] with `ComputeMultiplier.ofAuto()`. */ + fun computeMultiplierAuto() = computeMultiplier(ComputeMultiplier.ofAuto()) + + /** Alias for calling [computeMultiplier] with `ComputeMultiplier.ofNumber(number)`. */ + fun computeMultiplier(number: Double) = + computeMultiplier(ComputeMultiplier.ofNumber(number)) + + /** The number of training steps between evaluation runs. */ + fun evalInterval(evalInterval: EvalInterval) = evalInterval(JsonField.of(evalInterval)) + + /** + * Sets [Builder.evalInterval] to an arbitrary JSON value. + * + * You should usually call [Builder.evalInterval] with a well-typed [EvalInterval] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun evalInterval(evalInterval: JsonField) = apply { + this.evalInterval = evalInterval + } + + /** Alias for calling [evalInterval] with `EvalInterval.ofAuto()`. */ + fun evalIntervalAuto() = evalInterval(EvalInterval.ofAuto()) + + /** Alias for calling [evalInterval] with `EvalInterval.ofInteger(integer)`. */ + fun evalInterval(integer: Long) = evalInterval(EvalInterval.ofInteger(integer)) + + /** Number of evaluation samples to generate per training step. */ + fun evalSamples(evalSamples: EvalSamples) = evalSamples(JsonField.of(evalSamples)) + + /** + * Sets [Builder.evalSamples] to an arbitrary JSON value. + * + * You should usually call [Builder.evalSamples] with a well-typed [EvalSamples] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun evalSamples(evalSamples: JsonField) = apply { + this.evalSamples = evalSamples + } + + /** Alias for calling [evalSamples] with `EvalSamples.ofAuto()`. */ + fun evalSamplesAuto() = evalSamples(EvalSamples.ofAuto()) + + /** Alias for calling [evalSamples] with `EvalSamples.ofInteger(integer)`. */ + fun evalSamples(integer: Long) = evalSamples(EvalSamples.ofInteger(integer)) + + /** + * Scaling factor for the learning rate. A smaller learning rate may be useful to avoid + * overfitting. + */ + fun learningRateMultiplier(learningRateMultiplier: LearningRateMultiplier) = + learningRateMultiplier(JsonField.of(learningRateMultiplier)) + + /** + * Sets [Builder.learningRateMultiplier] to an arbitrary JSON value. + * + * You should usually call [Builder.learningRateMultiplier] with a well-typed + * [LearningRateMultiplier] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun learningRateMultiplier(learningRateMultiplier: JsonField) = + apply { + this.learningRateMultiplier = learningRateMultiplier + } + + /** Alias for calling [learningRateMultiplier] with `LearningRateMultiplier.ofAuto()`. */ + fun learningRateMultiplierAuto() = learningRateMultiplier(LearningRateMultiplier.ofAuto()) + + /** + * Alias for calling [learningRateMultiplier] with + * `LearningRateMultiplier.ofNumber(number)`. + */ + fun learningRateMultiplier(number: Double) = + learningRateMultiplier(LearningRateMultiplier.ofNumber(number)) + + /** + * The number of epochs to train the model for. An epoch refers to one full cycle through + * the training dataset. + */ + fun nEpochs(nEpochs: NEpochs) = nEpochs(JsonField.of(nEpochs)) + + /** + * Sets [Builder.nEpochs] to an arbitrary JSON value. + * + * You should usually call [Builder.nEpochs] with a well-typed [NEpochs] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun nEpochs(nEpochs: JsonField) = apply { this.nEpochs = nEpochs } + + /** Alias for calling [nEpochs] with `NEpochs.ofAuto()`. */ + fun nEpochsAuto() = nEpochs(NEpochs.ofAuto()) + + /** Alias for calling [nEpochs] with `NEpochs.ofInteger(integer)`. */ + fun nEpochs(integer: Long) = nEpochs(NEpochs.ofInteger(integer)) + + /** Level of reasoning effort. */ + fun reasoningEffort(reasoningEffort: ReasoningEffort) = + reasoningEffort(JsonField.of(reasoningEffort)) + + /** + * Sets [Builder.reasoningEffort] to an arbitrary JSON value. + * + * You should usually call [Builder.reasoningEffort] with a well-typed [ReasoningEffort] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun reasoningEffort(reasoningEffort: JsonField) = apply { + this.reasoningEffort = reasoningEffort + } + + 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) + } + + /** + * Returns an immutable instance of [ReinforcementHyperparameters]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ReinforcementHyperparameters = + ReinforcementHyperparameters( + batchSize, + computeMultiplier, + evalInterval, + evalSamples, + learningRateMultiplier, + nEpochs, + reasoningEffort, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ReinforcementHyperparameters = apply { + if (validated) { + return@apply + } + + batchSize().ifPresent { it.validate() } + computeMultiplier().ifPresent { it.validate() } + evalInterval().ifPresent { it.validate() } + evalSamples().ifPresent { it.validate() } + learningRateMultiplier().ifPresent { it.validate() } + nEpochs().ifPresent { it.validate() } + reasoningEffort().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (batchSize.asKnown().getOrNull()?.validity() ?: 0) + + (computeMultiplier.asKnown().getOrNull()?.validity() ?: 0) + + (evalInterval.asKnown().getOrNull()?.validity() ?: 0) + + (evalSamples.asKnown().getOrNull()?.validity() ?: 0) + + (learningRateMultiplier.asKnown().getOrNull()?.validity() ?: 0) + + (nEpochs.asKnown().getOrNull()?.validity() ?: 0) + + (reasoningEffort.asKnown().getOrNull()?.validity() ?: 0) + + /** + * Number of examples in each batch. A larger batch size means that model parameters are updated + * less frequently, but with lower variance. + */ + @JsonDeserialize(using = BatchSize.Deserializer::class) + @JsonSerialize(using = BatchSize.Serializer::class) + class BatchSize + private constructor( + private val auto: JsonValue? = null, + private val integer: Long? = null, + private val _json: JsonValue? = null, + ) { + + fun auto(): Optional = Optional.ofNullable(auto) + + fun integer(): Optional = Optional.ofNullable(integer) + + fun isAuto(): Boolean = auto != null + + fun isInteger(): Boolean = integer != null + + fun asAuto(): JsonValue = auto.getOrThrow("auto") + + fun asInteger(): Long = integer.getOrThrow("integer") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + auto != null -> visitor.visitAuto(auto) + integer != null -> visitor.visitInteger(integer) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): BatchSize = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) { + auto.let { + if (it != JsonValue.from("auto")) { + throw OpenAIInvalidDataException("'auto' is invalid, received $it") + } + } + } + + override fun visitInteger(integer: Long) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitInteger(integer: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is BatchSize && auto == other.auto && integer == other.integer /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, integer) /* spotless:on */ + + override fun toString(): String = + when { + auto != null -> "BatchSize{auto=$auto}" + integer != null -> "BatchSize{integer=$integer}" + _json != null -> "BatchSize{_unknown=$_json}" + else -> throw IllegalStateException("Invalid BatchSize") + } + + companion object { + + @JvmStatic fun ofAuto() = BatchSize(auto = JsonValue.from("auto")) + + @JvmStatic fun ofInteger(integer: Long) = BatchSize(integer = integer) + } + + /** + * An interface that defines how to map each variant of [BatchSize] to a value of type [T]. + */ + interface Visitor { + + fun visitAuto(auto: JsonValue): T + + fun visitInteger(integer: Long): T + + /** + * Maps an unknown variant of [BatchSize] to a value of type [T]. + * + * An instance of [BatchSize] 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 BatchSize: $json") + } + } + + internal class Deserializer : BaseDeserializer(BatchSize::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): BatchSize { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { BatchSize(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + BatchSize(integer = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from object). + 0 -> BatchSize(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(BatchSize::class) { + + override fun serialize( + value: BatchSize, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.auto != null -> generator.writeObject(value.auto) + value.integer != null -> generator.writeObject(value.integer) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid BatchSize") + } + } + } + } + + /** Multiplier on amount of compute used for exploring search space during training. */ + @JsonDeserialize(using = ComputeMultiplier.Deserializer::class) + @JsonSerialize(using = ComputeMultiplier.Serializer::class) + class ComputeMultiplier + private constructor( + private val auto: JsonValue? = null, + private val number: Double? = null, + private val _json: JsonValue? = null, + ) { + + fun auto(): Optional = Optional.ofNullable(auto) + + fun number(): Optional = Optional.ofNullable(number) + + fun isAuto(): Boolean = auto != null + + fun isNumber(): Boolean = number != null + + fun asAuto(): JsonValue = auto.getOrThrow("auto") + + fun asNumber(): Double = number.getOrThrow("number") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + auto != null -> visitor.visitAuto(auto) + number != null -> visitor.visitNumber(number) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): ComputeMultiplier = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) { + auto.let { + if (it != JsonValue.from("auto")) { + throw OpenAIInvalidDataException("'auto' is invalid, received $it") + } + } + } + + override fun visitNumber(number: Double) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitNumber(number: Double) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is ComputeMultiplier && auto == other.auto && number == other.number /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, number) /* spotless:on */ + + override fun toString(): String = + when { + auto != null -> "ComputeMultiplier{auto=$auto}" + number != null -> "ComputeMultiplier{number=$number}" + _json != null -> "ComputeMultiplier{_unknown=$_json}" + else -> throw IllegalStateException("Invalid ComputeMultiplier") + } + + companion object { + + @JvmStatic fun ofAuto() = ComputeMultiplier(auto = JsonValue.from("auto")) + + @JvmStatic fun ofNumber(number: Double) = ComputeMultiplier(number = number) + } + + /** + * An interface that defines how to map each variant of [ComputeMultiplier] to a value of + * type [T]. + */ + interface Visitor { + + fun visitAuto(auto: JsonValue): T + + fun visitNumber(number: Double): T + + /** + * Maps an unknown variant of [ComputeMultiplier] to a value of type [T]. + * + * An instance of [ComputeMultiplier] 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 ComputeMultiplier: $json") + } + } + + internal class Deserializer : + BaseDeserializer(ComputeMultiplier::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): ComputeMultiplier { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { ComputeMultiplier(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + ComputeMultiplier(number = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from object). + 0 -> ComputeMultiplier(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(ComputeMultiplier::class) { + + override fun serialize( + value: ComputeMultiplier, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.auto != null -> generator.writeObject(value.auto) + value.number != null -> generator.writeObject(value.number) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid ComputeMultiplier") + } + } + } + } + + /** The number of training steps between evaluation runs. */ + @JsonDeserialize(using = EvalInterval.Deserializer::class) + @JsonSerialize(using = EvalInterval.Serializer::class) + class EvalInterval + private constructor( + private val auto: JsonValue? = null, + private val integer: Long? = null, + private val _json: JsonValue? = null, + ) { + + fun auto(): Optional = Optional.ofNullable(auto) + + fun integer(): Optional = Optional.ofNullable(integer) + + fun isAuto(): Boolean = auto != null + + fun isInteger(): Boolean = integer != null + + fun asAuto(): JsonValue = auto.getOrThrow("auto") + + fun asInteger(): Long = integer.getOrThrow("integer") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + auto != null -> visitor.visitAuto(auto) + integer != null -> visitor.visitInteger(integer) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): EvalInterval = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) { + auto.let { + if (it != JsonValue.from("auto")) { + throw OpenAIInvalidDataException("'auto' is invalid, received $it") + } + } + } + + override fun visitInteger(integer: Long) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitInteger(integer: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is EvalInterval && auto == other.auto && integer == other.integer /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, integer) /* spotless:on */ + + override fun toString(): String = + when { + auto != null -> "EvalInterval{auto=$auto}" + integer != null -> "EvalInterval{integer=$integer}" + _json != null -> "EvalInterval{_unknown=$_json}" + else -> throw IllegalStateException("Invalid EvalInterval") + } + + companion object { + + @JvmStatic fun ofAuto() = EvalInterval(auto = JsonValue.from("auto")) + + @JvmStatic fun ofInteger(integer: Long) = EvalInterval(integer = integer) + } + + /** + * An interface that defines how to map each variant of [EvalInterval] to a value of type + * [T]. + */ + interface Visitor { + + fun visitAuto(auto: JsonValue): T + + fun visitInteger(integer: Long): T + + /** + * Maps an unknown variant of [EvalInterval] to a value of type [T]. + * + * An instance of [EvalInterval] 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 EvalInterval: $json") + } + } + + internal class Deserializer : BaseDeserializer(EvalInterval::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): EvalInterval { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { EvalInterval(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + EvalInterval(integer = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from object). + 0 -> EvalInterval(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(EvalInterval::class) { + + override fun serialize( + value: EvalInterval, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.auto != null -> generator.writeObject(value.auto) + value.integer != null -> generator.writeObject(value.integer) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid EvalInterval") + } + } + } + } + + /** Number of evaluation samples to generate per training step. */ + @JsonDeserialize(using = EvalSamples.Deserializer::class) + @JsonSerialize(using = EvalSamples.Serializer::class) + class EvalSamples + private constructor( + private val auto: JsonValue? = null, + private val integer: Long? = null, + private val _json: JsonValue? = null, + ) { + + fun auto(): Optional = Optional.ofNullable(auto) + + fun integer(): Optional = Optional.ofNullable(integer) + + fun isAuto(): Boolean = auto != null + + fun isInteger(): Boolean = integer != null + + fun asAuto(): JsonValue = auto.getOrThrow("auto") + + fun asInteger(): Long = integer.getOrThrow("integer") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + auto != null -> visitor.visitAuto(auto) + integer != null -> visitor.visitInteger(integer) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): EvalSamples = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) { + auto.let { + if (it != JsonValue.from("auto")) { + throw OpenAIInvalidDataException("'auto' is invalid, received $it") + } + } + } + + override fun visitInteger(integer: Long) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitInteger(integer: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is EvalSamples && auto == other.auto && integer == other.integer /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, integer) /* spotless:on */ + + override fun toString(): String = + when { + auto != null -> "EvalSamples{auto=$auto}" + integer != null -> "EvalSamples{integer=$integer}" + _json != null -> "EvalSamples{_unknown=$_json}" + else -> throw IllegalStateException("Invalid EvalSamples") + } + + companion object { + + @JvmStatic fun ofAuto() = EvalSamples(auto = JsonValue.from("auto")) + + @JvmStatic fun ofInteger(integer: Long) = EvalSamples(integer = integer) + } + + /** + * An interface that defines how to map each variant of [EvalSamples] to a value of type + * [T]. + */ + interface Visitor { + + fun visitAuto(auto: JsonValue): T + + fun visitInteger(integer: Long): T + + /** + * Maps an unknown variant of [EvalSamples] to a value of type [T]. + * + * An instance of [EvalSamples] 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 EvalSamples: $json") + } + } + + internal class Deserializer : BaseDeserializer(EvalSamples::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): EvalSamples { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { EvalSamples(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + EvalSamples(integer = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from object). + 0 -> EvalSamples(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(EvalSamples::class) { + + override fun serialize( + value: EvalSamples, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.auto != null -> generator.writeObject(value.auto) + value.integer != null -> generator.writeObject(value.integer) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid EvalSamples") + } + } + } + } + + /** + * Scaling factor for the learning rate. A smaller learning rate may be useful to avoid + * overfitting. + */ + @JsonDeserialize(using = LearningRateMultiplier.Deserializer::class) + @JsonSerialize(using = LearningRateMultiplier.Serializer::class) + class LearningRateMultiplier + private constructor( + private val auto: JsonValue? = null, + private val number: Double? = null, + private val _json: JsonValue? = null, + ) { + + fun auto(): Optional = Optional.ofNullable(auto) + + fun number(): Optional = Optional.ofNullable(number) + + fun isAuto(): Boolean = auto != null + + fun isNumber(): Boolean = number != null + + fun asAuto(): JsonValue = auto.getOrThrow("auto") + + fun asNumber(): Double = number.getOrThrow("number") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + auto != null -> visitor.visitAuto(auto) + number != null -> visitor.visitNumber(number) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): LearningRateMultiplier = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) { + auto.let { + if (it != JsonValue.from("auto")) { + throw OpenAIInvalidDataException("'auto' is invalid, received $it") + } + } + } + + override fun visitNumber(number: Double) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitNumber(number: Double) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is LearningRateMultiplier && auto == other.auto && number == other.number /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, number) /* spotless:on */ + + override fun toString(): String = + when { + auto != null -> "LearningRateMultiplier{auto=$auto}" + number != null -> "LearningRateMultiplier{number=$number}" + _json != null -> "LearningRateMultiplier{_unknown=$_json}" + else -> throw IllegalStateException("Invalid LearningRateMultiplier") + } + + companion object { + + @JvmStatic fun ofAuto() = LearningRateMultiplier(auto = JsonValue.from("auto")) + + @JvmStatic fun ofNumber(number: Double) = LearningRateMultiplier(number = number) + } + + /** + * An interface that defines how to map each variant of [LearningRateMultiplier] to a value + * of type [T]. + */ + interface Visitor { + + fun visitAuto(auto: JsonValue): T + + fun visitNumber(number: Double): T + + /** + * Maps an unknown variant of [LearningRateMultiplier] to a value of type [T]. + * + * An instance of [LearningRateMultiplier] 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 LearningRateMultiplier: $json") + } + } + + internal class Deserializer : + BaseDeserializer(LearningRateMultiplier::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): LearningRateMultiplier { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { LearningRateMultiplier(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + LearningRateMultiplier(number = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from object). + 0 -> LearningRateMultiplier(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(LearningRateMultiplier::class) { + + override fun serialize( + value: LearningRateMultiplier, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.auto != null -> generator.writeObject(value.auto) + value.number != null -> generator.writeObject(value.number) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid LearningRateMultiplier") + } + } + } + } + + /** + * The number of epochs to train the model for. An epoch refers to one full cycle through the + * training dataset. + */ + @JsonDeserialize(using = NEpochs.Deserializer::class) + @JsonSerialize(using = NEpochs.Serializer::class) + class NEpochs + private constructor( + private val auto: JsonValue? = null, + private val integer: Long? = null, + private val _json: JsonValue? = null, + ) { + + fun auto(): Optional = Optional.ofNullable(auto) + + fun integer(): Optional = Optional.ofNullable(integer) + + fun isAuto(): Boolean = auto != null + + fun isInteger(): Boolean = integer != null + + fun asAuto(): JsonValue = auto.getOrThrow("auto") + + fun asInteger(): Long = integer.getOrThrow("integer") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + auto != null -> visitor.visitAuto(auto) + integer != null -> visitor.visitInteger(integer) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): NEpochs = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) { + auto.let { + if (it != JsonValue.from("auto")) { + throw OpenAIInvalidDataException("'auto' is invalid, received $it") + } + } + } + + override fun visitInteger(integer: Long) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitInteger(integer: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is NEpochs && auto == other.auto && integer == other.integer /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, integer) /* spotless:on */ + + override fun toString(): String = + when { + auto != null -> "NEpochs{auto=$auto}" + integer != null -> "NEpochs{integer=$integer}" + _json != null -> "NEpochs{_unknown=$_json}" + else -> throw IllegalStateException("Invalid NEpochs") + } + + companion object { + + @JvmStatic fun ofAuto() = NEpochs(auto = JsonValue.from("auto")) + + @JvmStatic fun ofInteger(integer: Long) = NEpochs(integer = integer) + } + + /** + * An interface that defines how to map each variant of [NEpochs] to a value of type [T]. + */ + interface Visitor { + + fun visitAuto(auto: JsonValue): T + + fun visitInteger(integer: Long): T + + /** + * Maps an unknown variant of [NEpochs] to a value of type [T]. + * + * An instance of [NEpochs] 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 NEpochs: $json") + } + } + + internal class Deserializer : BaseDeserializer(NEpochs::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): NEpochs { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { NEpochs(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + NEpochs(integer = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from object). + 0 -> NEpochs(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(NEpochs::class) { + + override fun serialize( + value: NEpochs, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.auto != null -> generator.writeObject(value.auto) + value.integer != null -> generator.writeObject(value.integer) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid NEpochs") + } + } + } + } + + /** Level of reasoning effort. */ + class ReasoningEffort @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 DEFAULT = of("default") + + @JvmField val LOW = of("low") + + @JvmField val MEDIUM = of("medium") + + @JvmField val HIGH = of("high") + + @JvmStatic fun of(value: String) = ReasoningEffort(JsonField.of(value)) + } + + /** An enum containing [ReasoningEffort]'s known values. */ + enum class Known { + DEFAULT, + LOW, + MEDIUM, + HIGH, + } + + /** + * An enum containing [ReasoningEffort]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [ReasoningEffort] 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 { + DEFAULT, + LOW, + MEDIUM, + HIGH, + /** + * An enum member indicating that [ReasoningEffort] 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) { + DEFAULT -> Value.DEFAULT + LOW -> Value.LOW + MEDIUM -> Value.MEDIUM + HIGH -> Value.HIGH + 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) { + DEFAULT -> Known.DEFAULT + LOW -> Known.LOW + MEDIUM -> Known.MEDIUM + HIGH -> Known.HIGH + else -> throw OpenAIInvalidDataException("Unknown ReasoningEffort: $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") } + + private var validated: Boolean = false + + fun validate(): ReasoningEffort = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is ReasoningEffort && 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 ReinforcementHyperparameters && batchSize == other.batchSize && computeMultiplier == other.computeMultiplier && evalInterval == other.evalInterval && evalSamples == other.evalSamples && learningRateMultiplier == other.learningRateMultiplier && nEpochs == other.nEpochs && reasoningEffort == other.reasoningEffort && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(batchSize, computeMultiplier, evalInterval, evalSamples, learningRateMultiplier, nEpochs, reasoningEffort, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ReinforcementHyperparameters{batchSize=$batchSize, computeMultiplier=$computeMultiplier, evalInterval=$evalInterval, evalSamples=$evalSamples, learningRateMultiplier=$learningRateMultiplier, nEpochs=$nEpochs, reasoningEffort=$reasoningEffort, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/ReinforcementMethod.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/ReinforcementMethod.kt new file mode 100644 index 00000000..423082ee --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/ReinforcementMethod.kt @@ -0,0 +1,541 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.methods + +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.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.ExcludeMissing +import com.openai.core.JsonField +import com.openai.core.JsonMissing +import com.openai.core.JsonValue +import com.openai.core.allMaxBy +import com.openai.core.checkRequired +import com.openai.core.getOrThrow +import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.graders.gradermodels.MultiGrader +import com.openai.models.graders.gradermodels.PythonGrader +import com.openai.models.graders.gradermodels.ScoreModelGrader +import com.openai.models.graders.gradermodels.StringCheckGrader +import com.openai.models.graders.gradermodels.TextSimilarityGrader +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** Configuration for the reinforcement fine-tuning method. */ +class ReinforcementMethod +private constructor( + private val grader: JsonField, + private val hyperparameters: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("grader") @ExcludeMissing grader: JsonField = JsonMissing.of(), + @JsonProperty("hyperparameters") + @ExcludeMissing + hyperparameters: JsonField = JsonMissing.of(), + ) : this(grader, hyperparameters, mutableMapOf()) + + /** + * The grader used for the fine-tuning job. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun grader(): Grader = grader.getRequired("grader") + + /** + * The hyperparameters used for the reinforcement fine-tuning job. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun hyperparameters(): Optional = + hyperparameters.getOptional("hyperparameters") + + /** + * Returns the raw JSON value of [grader]. + * + * Unlike [grader], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("grader") @ExcludeMissing fun _grader(): JsonField = grader + + /** + * Returns the raw JSON value of [hyperparameters]. + * + * Unlike [hyperparameters], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hyperparameters") + @ExcludeMissing + fun _hyperparameters(): JsonField = hyperparameters + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ReinforcementMethod]. + * + * The following fields are required: + * ```java + * .grader() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ReinforcementMethod]. */ + class Builder internal constructor() { + + private var grader: JsonField? = null + private var hyperparameters: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(reinforcementMethod: ReinforcementMethod) = apply { + grader = reinforcementMethod.grader + hyperparameters = reinforcementMethod.hyperparameters + additionalProperties = reinforcementMethod.additionalProperties.toMutableMap() + } + + /** The grader used for the fine-tuning job. */ + fun grader(grader: Grader) = grader(JsonField.of(grader)) + + /** + * Sets [Builder.grader] to an arbitrary JSON value. + * + * You should usually call [Builder.grader] with a well-typed [Grader] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun grader(grader: JsonField) = apply { this.grader = grader } + + /** Alias for calling [grader] with `Grader.ofStringCheck(stringCheck)`. */ + fun grader(stringCheck: StringCheckGrader) = grader(Grader.ofStringCheck(stringCheck)) + + /** Alias for calling [grader] with `Grader.ofTextSimilarity(textSimilarity)`. */ + fun grader(textSimilarity: TextSimilarityGrader) = + grader(Grader.ofTextSimilarity(textSimilarity)) + + /** Alias for calling [grader] with `Grader.ofPython(python)`. */ + fun grader(python: PythonGrader) = grader(Grader.ofPython(python)) + + /** Alias for calling [grader] with `Grader.ofScoreModel(scoreModel)`. */ + fun grader(scoreModel: ScoreModelGrader) = grader(Grader.ofScoreModel(scoreModel)) + + /** Alias for calling [grader] with `Grader.ofMulti(multi)`. */ + fun grader(multi: MultiGrader) = grader(Grader.ofMulti(multi)) + + /** The hyperparameters used for the reinforcement fine-tuning job. */ + fun hyperparameters(hyperparameters: ReinforcementHyperparameters) = + hyperparameters(JsonField.of(hyperparameters)) + + /** + * Sets [Builder.hyperparameters] to an arbitrary JSON value. + * + * You should usually call [Builder.hyperparameters] with a well-typed + * [ReinforcementHyperparameters] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun hyperparameters(hyperparameters: JsonField) = apply { + this.hyperparameters = hyperparameters + } + + 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) + } + + /** + * Returns an immutable instance of [ReinforcementMethod]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .grader() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ReinforcementMethod = + ReinforcementMethod( + checkRequired("grader", grader), + hyperparameters, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ReinforcementMethod = apply { + if (validated) { + return@apply + } + + grader().validate() + hyperparameters().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (grader.asKnown().getOrNull()?.validity() ?: 0) + + (hyperparameters.asKnown().getOrNull()?.validity() ?: 0) + + /** The grader used for the fine-tuning job. */ + @JsonDeserialize(using = Grader.Deserializer::class) + @JsonSerialize(using = Grader.Serializer::class) + class Grader + private constructor( + private val stringCheck: StringCheckGrader? = null, + private val textSimilarity: TextSimilarityGrader? = null, + private val python: PythonGrader? = null, + private val scoreModel: ScoreModelGrader? = null, + private val multi: MultiGrader? = null, + private val _json: JsonValue? = null, + ) { + + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun stringCheck(): Optional = Optional.ofNullable(stringCheck) + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun textSimilarity(): Optional = Optional.ofNullable(textSimilarity) + + /** A PythonGrader object that runs a python script on the input. */ + fun python(): Optional = Optional.ofNullable(python) + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun scoreModel(): Optional = Optional.ofNullable(scoreModel) + + /** + * A MultiGrader object combines the output of multiple graders to produce a single score. + */ + fun multi(): Optional = Optional.ofNullable(multi) + + fun isStringCheck(): Boolean = stringCheck != null + + fun isTextSimilarity(): Boolean = textSimilarity != null + + fun isPython(): Boolean = python != null + + fun isScoreModel(): Boolean = scoreModel != null + + fun isMulti(): Boolean = multi != null + + /** + * A StringCheckGrader object that performs a string comparison between input and reference + * using a specified operation. + */ + fun asStringCheck(): StringCheckGrader = stringCheck.getOrThrow("stringCheck") + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun asTextSimilarity(): TextSimilarityGrader = textSimilarity.getOrThrow("textSimilarity") + + /** A PythonGrader object that runs a python script on the input. */ + fun asPython(): PythonGrader = python.getOrThrow("python") + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun asScoreModel(): ScoreModelGrader = scoreModel.getOrThrow("scoreModel") + + /** + * A MultiGrader object combines the output of multiple graders to produce a single score. + */ + fun asMulti(): MultiGrader = multi.getOrThrow("multi") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + stringCheck != null -> visitor.visitStringCheck(stringCheck) + textSimilarity != null -> visitor.visitTextSimilarity(textSimilarity) + python != null -> visitor.visitPython(python) + scoreModel != null -> visitor.visitScoreModel(scoreModel) + multi != null -> visitor.visitMulti(multi) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Grader = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitStringCheck(stringCheck: StringCheckGrader) { + stringCheck.validate() + } + + override fun visitTextSimilarity(textSimilarity: TextSimilarityGrader) { + textSimilarity.validate() + } + + override fun visitPython(python: PythonGrader) { + python.validate() + } + + override fun visitScoreModel(scoreModel: ScoreModelGrader) { + scoreModel.validate() + } + + override fun visitMulti(multi: MultiGrader) { + multi.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitStringCheck(stringCheck: StringCheckGrader) = + stringCheck.validity() + + override fun visitTextSimilarity(textSimilarity: TextSimilarityGrader) = + textSimilarity.validity() + + override fun visitPython(python: PythonGrader) = python.validity() + + override fun visitScoreModel(scoreModel: ScoreModelGrader) = + scoreModel.validity() + + override fun visitMulti(multi: MultiGrader) = multi.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Grader && stringCheck == other.stringCheck && textSimilarity == other.textSimilarity && python == other.python && scoreModel == other.scoreModel && multi == other.multi /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(stringCheck, textSimilarity, python, scoreModel, multi) /* spotless:on */ + + override fun toString(): String = + when { + stringCheck != null -> "Grader{stringCheck=$stringCheck}" + textSimilarity != null -> "Grader{textSimilarity=$textSimilarity}" + python != null -> "Grader{python=$python}" + scoreModel != null -> "Grader{scoreModel=$scoreModel}" + multi != null -> "Grader{multi=$multi}" + _json != null -> "Grader{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Grader") + } + + companion object { + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + @JvmStatic + fun ofStringCheck(stringCheck: StringCheckGrader) = Grader(stringCheck = stringCheck) + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + @JvmStatic + fun ofTextSimilarity(textSimilarity: TextSimilarityGrader) = + Grader(textSimilarity = textSimilarity) + + /** A PythonGrader object that runs a python script on the input. */ + @JvmStatic fun ofPython(python: PythonGrader) = Grader(python = python) + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + @JvmStatic + fun ofScoreModel(scoreModel: ScoreModelGrader) = Grader(scoreModel = scoreModel) + + /** + * A MultiGrader object combines the output of multiple graders to produce a single + * score. + */ + @JvmStatic fun ofMulti(multi: MultiGrader) = Grader(multi = multi) + } + + /** An interface that defines how to map each variant of [Grader] to a value of type [T]. */ + interface Visitor { + + /** + * A StringCheckGrader object that performs a string comparison between input and + * reference using a specified operation. + */ + fun visitStringCheck(stringCheck: StringCheckGrader): T + + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + fun visitTextSimilarity(textSimilarity: TextSimilarityGrader): T + + /** A PythonGrader object that runs a python script on the input. */ + fun visitPython(python: PythonGrader): T + + /** A ScoreModelGrader object that uses a model to assign a score to the input. */ + fun visitScoreModel(scoreModel: ScoreModelGrader): T + + /** + * A MultiGrader object combines the output of multiple graders to produce a single + * score. + */ + fun visitMulti(multi: MultiGrader): T + + /** + * Maps an unknown variant of [Grader] to a value of type [T]. + * + * An instance of [Grader] 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 Grader: $json") + } + } + + internal class Deserializer : BaseDeserializer(Grader::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Grader { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Grader(stringCheck = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Grader(textSimilarity = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Grader(python = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Grader(scoreModel = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Grader(multi = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> Grader(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Grader::class) { + + override fun serialize( + value: Grader, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.stringCheck != null -> generator.writeObject(value.stringCheck) + value.textSimilarity != null -> generator.writeObject(value.textSimilarity) + value.python != null -> generator.writeObject(value.python) + value.scoreModel != null -> generator.writeObject(value.scoreModel) + value.multi != null -> generator.writeObject(value.multi) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Grader") + } + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is ReinforcementMethod && grader == other.grader && hyperparameters == other.hyperparameters && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(grader, hyperparameters, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ReinforcementMethod{grader=$grader, hyperparameters=$hyperparameters, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/SupervisedHyperparameters.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/SupervisedHyperparameters.kt new file mode 100644 index 00000000..92aa0e79 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/SupervisedHyperparameters.kt @@ -0,0 +1,832 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.methods + +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.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.ExcludeMissing +import com.openai.core.JsonField +import com.openai.core.JsonMissing +import com.openai.core.JsonValue +import com.openai.core.allMaxBy +import com.openai.core.getOrThrow +import com.openai.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** The hyperparameters used for the fine-tuning job. */ +class SupervisedHyperparameters +private constructor( + private val batchSize: JsonField, + private val learningRateMultiplier: JsonField, + private val nEpochs: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("batch_size") + @ExcludeMissing + batchSize: JsonField = JsonMissing.of(), + @JsonProperty("learning_rate_multiplier") + @ExcludeMissing + learningRateMultiplier: JsonField = JsonMissing.of(), + @JsonProperty("n_epochs") @ExcludeMissing nEpochs: JsonField = JsonMissing.of(), + ) : this(batchSize, learningRateMultiplier, nEpochs, mutableMapOf()) + + /** + * Number of examples in each batch. A larger batch size means that model parameters are updated + * less frequently, but with lower variance. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun batchSize(): Optional = batchSize.getOptional("batch_size") + + /** + * Scaling factor for the learning rate. A smaller learning rate may be useful to avoid + * overfitting. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun learningRateMultiplier(): Optional = + learningRateMultiplier.getOptional("learning_rate_multiplier") + + /** + * The number of epochs to train the model for. An epoch refers to one full cycle through the + * training dataset. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun nEpochs(): Optional = nEpochs.getOptional("n_epochs") + + /** + * Returns the raw JSON value of [batchSize]. + * + * Unlike [batchSize], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("batch_size") @ExcludeMissing fun _batchSize(): JsonField = batchSize + + /** + * Returns the raw JSON value of [learningRateMultiplier]. + * + * Unlike [learningRateMultiplier], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("learning_rate_multiplier") + @ExcludeMissing + fun _learningRateMultiplier(): JsonField = learningRateMultiplier + + /** + * Returns the raw JSON value of [nEpochs]. + * + * Unlike [nEpochs], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("n_epochs") @ExcludeMissing fun _nEpochs(): JsonField = nEpochs + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [SupervisedHyperparameters]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [SupervisedHyperparameters]. */ + class Builder internal constructor() { + + private var batchSize: JsonField = JsonMissing.of() + private var learningRateMultiplier: JsonField = JsonMissing.of() + private var nEpochs: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(supervisedHyperparameters: SupervisedHyperparameters) = apply { + batchSize = supervisedHyperparameters.batchSize + learningRateMultiplier = supervisedHyperparameters.learningRateMultiplier + nEpochs = supervisedHyperparameters.nEpochs + additionalProperties = supervisedHyperparameters.additionalProperties.toMutableMap() + } + + /** + * Number of examples in each batch. A larger batch size means that model parameters are + * updated less frequently, but with lower variance. + */ + fun batchSize(batchSize: BatchSize) = batchSize(JsonField.of(batchSize)) + + /** + * Sets [Builder.batchSize] to an arbitrary JSON value. + * + * You should usually call [Builder.batchSize] with a well-typed [BatchSize] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun batchSize(batchSize: JsonField) = apply { this.batchSize = batchSize } + + /** Alias for calling [batchSize] with `BatchSize.ofAuto()`. */ + fun batchSizeAuto() = batchSize(BatchSize.ofAuto()) + + /** Alias for calling [batchSize] with `BatchSize.ofInteger(integer)`. */ + fun batchSize(integer: Long) = batchSize(BatchSize.ofInteger(integer)) + + /** + * Scaling factor for the learning rate. A smaller learning rate may be useful to avoid + * overfitting. + */ + fun learningRateMultiplier(learningRateMultiplier: LearningRateMultiplier) = + learningRateMultiplier(JsonField.of(learningRateMultiplier)) + + /** + * Sets [Builder.learningRateMultiplier] to an arbitrary JSON value. + * + * You should usually call [Builder.learningRateMultiplier] with a well-typed + * [LearningRateMultiplier] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun learningRateMultiplier(learningRateMultiplier: JsonField) = + apply { + this.learningRateMultiplier = learningRateMultiplier + } + + /** Alias for calling [learningRateMultiplier] with `LearningRateMultiplier.ofAuto()`. */ + fun learningRateMultiplierAuto() = learningRateMultiplier(LearningRateMultiplier.ofAuto()) + + /** + * Alias for calling [learningRateMultiplier] with + * `LearningRateMultiplier.ofNumber(number)`. + */ + fun learningRateMultiplier(number: Double) = + learningRateMultiplier(LearningRateMultiplier.ofNumber(number)) + + /** + * The number of epochs to train the model for. An epoch refers to one full cycle through + * the training dataset. + */ + fun nEpochs(nEpochs: NEpochs) = nEpochs(JsonField.of(nEpochs)) + + /** + * Sets [Builder.nEpochs] to an arbitrary JSON value. + * + * You should usually call [Builder.nEpochs] with a well-typed [NEpochs] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun nEpochs(nEpochs: JsonField) = apply { this.nEpochs = nEpochs } + + /** Alias for calling [nEpochs] with `NEpochs.ofAuto()`. */ + fun nEpochsAuto() = nEpochs(NEpochs.ofAuto()) + + /** Alias for calling [nEpochs] with `NEpochs.ofInteger(integer)`. */ + fun nEpochs(integer: Long) = nEpochs(NEpochs.ofInteger(integer)) + + 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) + } + + /** + * Returns an immutable instance of [SupervisedHyperparameters]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): SupervisedHyperparameters = + SupervisedHyperparameters( + batchSize, + learningRateMultiplier, + nEpochs, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): SupervisedHyperparameters = apply { + if (validated) { + return@apply + } + + batchSize().ifPresent { it.validate() } + learningRateMultiplier().ifPresent { it.validate() } + nEpochs().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (batchSize.asKnown().getOrNull()?.validity() ?: 0) + + (learningRateMultiplier.asKnown().getOrNull()?.validity() ?: 0) + + (nEpochs.asKnown().getOrNull()?.validity() ?: 0) + + /** + * Number of examples in each batch. A larger batch size means that model parameters are updated + * less frequently, but with lower variance. + */ + @JsonDeserialize(using = BatchSize.Deserializer::class) + @JsonSerialize(using = BatchSize.Serializer::class) + class BatchSize + private constructor( + private val auto: JsonValue? = null, + private val integer: Long? = null, + private val _json: JsonValue? = null, + ) { + + fun auto(): Optional = Optional.ofNullable(auto) + + fun integer(): Optional = Optional.ofNullable(integer) + + fun isAuto(): Boolean = auto != null + + fun isInteger(): Boolean = integer != null + + fun asAuto(): JsonValue = auto.getOrThrow("auto") + + fun asInteger(): Long = integer.getOrThrow("integer") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + auto != null -> visitor.visitAuto(auto) + integer != null -> visitor.visitInteger(integer) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): BatchSize = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) { + auto.let { + if (it != JsonValue.from("auto")) { + throw OpenAIInvalidDataException("'auto' is invalid, received $it") + } + } + } + + override fun visitInteger(integer: Long) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitInteger(integer: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is BatchSize && auto == other.auto && integer == other.integer /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, integer) /* spotless:on */ + + override fun toString(): String = + when { + auto != null -> "BatchSize{auto=$auto}" + integer != null -> "BatchSize{integer=$integer}" + _json != null -> "BatchSize{_unknown=$_json}" + else -> throw IllegalStateException("Invalid BatchSize") + } + + companion object { + + @JvmStatic fun ofAuto() = BatchSize(auto = JsonValue.from("auto")) + + @JvmStatic fun ofInteger(integer: Long) = BatchSize(integer = integer) + } + + /** + * An interface that defines how to map each variant of [BatchSize] to a value of type [T]. + */ + interface Visitor { + + fun visitAuto(auto: JsonValue): T + + fun visitInteger(integer: Long): T + + /** + * Maps an unknown variant of [BatchSize] to a value of type [T]. + * + * An instance of [BatchSize] 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 BatchSize: $json") + } + } + + internal class Deserializer : BaseDeserializer(BatchSize::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): BatchSize { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { BatchSize(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + BatchSize(integer = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from object). + 0 -> BatchSize(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(BatchSize::class) { + + override fun serialize( + value: BatchSize, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.auto != null -> generator.writeObject(value.auto) + value.integer != null -> generator.writeObject(value.integer) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid BatchSize") + } + } + } + } + + /** + * Scaling factor for the learning rate. A smaller learning rate may be useful to avoid + * overfitting. + */ + @JsonDeserialize(using = LearningRateMultiplier.Deserializer::class) + @JsonSerialize(using = LearningRateMultiplier.Serializer::class) + class LearningRateMultiplier + private constructor( + private val auto: JsonValue? = null, + private val number: Double? = null, + private val _json: JsonValue? = null, + ) { + + fun auto(): Optional = Optional.ofNullable(auto) + + fun number(): Optional = Optional.ofNullable(number) + + fun isAuto(): Boolean = auto != null + + fun isNumber(): Boolean = number != null + + fun asAuto(): JsonValue = auto.getOrThrow("auto") + + fun asNumber(): Double = number.getOrThrow("number") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + auto != null -> visitor.visitAuto(auto) + number != null -> visitor.visitNumber(number) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): LearningRateMultiplier = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) { + auto.let { + if (it != JsonValue.from("auto")) { + throw OpenAIInvalidDataException("'auto' is invalid, received $it") + } + } + } + + override fun visitNumber(number: Double) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitNumber(number: Double) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is LearningRateMultiplier && auto == other.auto && number == other.number /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, number) /* spotless:on */ + + override fun toString(): String = + when { + auto != null -> "LearningRateMultiplier{auto=$auto}" + number != null -> "LearningRateMultiplier{number=$number}" + _json != null -> "LearningRateMultiplier{_unknown=$_json}" + else -> throw IllegalStateException("Invalid LearningRateMultiplier") + } + + companion object { + + @JvmStatic fun ofAuto() = LearningRateMultiplier(auto = JsonValue.from("auto")) + + @JvmStatic fun ofNumber(number: Double) = LearningRateMultiplier(number = number) + } + + /** + * An interface that defines how to map each variant of [LearningRateMultiplier] to a value + * of type [T]. + */ + interface Visitor { + + fun visitAuto(auto: JsonValue): T + + fun visitNumber(number: Double): T + + /** + * Maps an unknown variant of [LearningRateMultiplier] to a value of type [T]. + * + * An instance of [LearningRateMultiplier] 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 LearningRateMultiplier: $json") + } + } + + internal class Deserializer : + BaseDeserializer(LearningRateMultiplier::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): LearningRateMultiplier { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { LearningRateMultiplier(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + LearningRateMultiplier(number = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from object). + 0 -> LearningRateMultiplier(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(LearningRateMultiplier::class) { + + override fun serialize( + value: LearningRateMultiplier, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.auto != null -> generator.writeObject(value.auto) + value.number != null -> generator.writeObject(value.number) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid LearningRateMultiplier") + } + } + } + } + + /** + * The number of epochs to train the model for. An epoch refers to one full cycle through the + * training dataset. + */ + @JsonDeserialize(using = NEpochs.Deserializer::class) + @JsonSerialize(using = NEpochs.Serializer::class) + class NEpochs + private constructor( + private val auto: JsonValue? = null, + private val integer: Long? = null, + private val _json: JsonValue? = null, + ) { + + fun auto(): Optional = Optional.ofNullable(auto) + + fun integer(): Optional = Optional.ofNullable(integer) + + fun isAuto(): Boolean = auto != null + + fun isInteger(): Boolean = integer != null + + fun asAuto(): JsonValue = auto.getOrThrow("auto") + + fun asInteger(): Long = integer.getOrThrow("integer") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + auto != null -> visitor.visitAuto(auto) + integer != null -> visitor.visitInteger(integer) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): NEpochs = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) { + auto.let { + if (it != JsonValue.from("auto")) { + throw OpenAIInvalidDataException("'auto' is invalid, received $it") + } + } + } + + override fun visitInteger(integer: Long) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAuto(auto: JsonValue) = + auto.let { if (it == JsonValue.from("auto")) 1 else 0 } + + override fun visitInteger(integer: Long) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is NEpochs && auto == other.auto && integer == other.integer /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(auto, integer) /* spotless:on */ + + override fun toString(): String = + when { + auto != null -> "NEpochs{auto=$auto}" + integer != null -> "NEpochs{integer=$integer}" + _json != null -> "NEpochs{_unknown=$_json}" + else -> throw IllegalStateException("Invalid NEpochs") + } + + companion object { + + @JvmStatic fun ofAuto() = NEpochs(auto = JsonValue.from("auto")) + + @JvmStatic fun ofInteger(integer: Long) = NEpochs(integer = integer) + } + + /** + * An interface that defines how to map each variant of [NEpochs] to a value of type [T]. + */ + interface Visitor { + + fun visitAuto(auto: JsonValue): T + + fun visitInteger(integer: Long): T + + /** + * Maps an unknown variant of [NEpochs] to a value of type [T]. + * + * An instance of [NEpochs] 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 NEpochs: $json") + } + } + + internal class Deserializer : BaseDeserializer(NEpochs::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): NEpochs { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { NEpochs(auto = it, _json = json) } + ?.takeIf { it.isValid() }, + tryDeserialize(node, jacksonTypeRef())?.let { + NEpochs(integer = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from object). + 0 -> NEpochs(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(NEpochs::class) { + + override fun serialize( + value: NEpochs, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.auto != null -> generator.writeObject(value.auto) + value.integer != null -> generator.writeObject(value.integer) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid NEpochs") + } + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is SupervisedHyperparameters && batchSize == other.batchSize && learningRateMultiplier == other.learningRateMultiplier && nEpochs == other.nEpochs && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(batchSize, learningRateMultiplier, nEpochs, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "SupervisedHyperparameters{batchSize=$batchSize, learningRateMultiplier=$learningRateMultiplier, nEpochs=$nEpochs, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/SupervisedMethod.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/SupervisedMethod.kt new file mode 100644 index 00000000..e6aa5f12 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/SupervisedMethod.kt @@ -0,0 +1,167 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.methods + +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.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** Configuration for the supervised fine-tuning method. */ +class SupervisedMethod +private constructor( + private val hyperparameters: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("hyperparameters") + @ExcludeMissing + hyperparameters: JsonField = JsonMissing.of() + ) : this(hyperparameters, mutableMapOf()) + + /** + * The hyperparameters used for the fine-tuning job. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun hyperparameters(): Optional = + hyperparameters.getOptional("hyperparameters") + + /** + * Returns the raw JSON value of [hyperparameters]. + * + * Unlike [hyperparameters], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hyperparameters") + @ExcludeMissing + fun _hyperparameters(): JsonField = hyperparameters + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [SupervisedMethod]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [SupervisedMethod]. */ + class Builder internal constructor() { + + private var hyperparameters: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(supervisedMethod: SupervisedMethod) = apply { + hyperparameters = supervisedMethod.hyperparameters + additionalProperties = supervisedMethod.additionalProperties.toMutableMap() + } + + /** The hyperparameters used for the fine-tuning job. */ + fun hyperparameters(hyperparameters: SupervisedHyperparameters) = + hyperparameters(JsonField.of(hyperparameters)) + + /** + * Sets [Builder.hyperparameters] to an arbitrary JSON value. + * + * You should usually call [Builder.hyperparameters] with a well-typed + * [SupervisedHyperparameters] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun hyperparameters(hyperparameters: JsonField) = apply { + this.hyperparameters = hyperparameters + } + + 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) + } + + /** + * Returns an immutable instance of [SupervisedMethod]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): SupervisedMethod = + SupervisedMethod(hyperparameters, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): SupervisedMethod = apply { + if (validated) { + return@apply + } + + hyperparameters().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = (hyperparameters.asKnown().getOrNull()?.validity() ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is SupervisedMethod && hyperparameters == other.hyperparameters && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(hyperparameters, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "SupervisedMethod{hyperparameters=$hyperparameters, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalLabelModelGrader.kt b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/LabelModelGrader.kt similarity index 97% rename from openai-java-core/src/main/kotlin/com/openai/models/evals/EvalLabelModelGrader.kt rename to openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/LabelModelGrader.kt index 7576d0c9..e29ecd15 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalLabelModelGrader.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/LabelModelGrader.kt @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. -package com.openai.models.evals +package com.openai.models.graders.gradermodels import com.fasterxml.jackson.annotation.JsonAnyGetter import com.fasterxml.jackson.annotation.JsonAnySetter @@ -33,7 +33,7 @@ import java.util.Optional import kotlin.jvm.optionals.getOrNull /** A LabelModelGrader object which uses a model to assign labels to each item in the evaluation. */ -class EvalLabelModelGrader +class LabelModelGrader private constructor( private val input: JsonField>, private val labels: JsonField>, @@ -159,7 +159,7 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [EvalLabelModelGrader]. + * Returns a mutable builder for constructing an instance of [LabelModelGrader]. * * The following fields are required: * ```java @@ -173,7 +173,7 @@ private constructor( @JvmStatic fun builder() = Builder() } - /** A builder for [EvalLabelModelGrader]. */ + /** A builder for [LabelModelGrader]. */ class Builder internal constructor() { private var input: JsonField>? = null @@ -185,14 +185,14 @@ private constructor( private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(evalLabelModelGrader: EvalLabelModelGrader) = apply { - input = evalLabelModelGrader.input.map { it.toMutableList() } - labels = evalLabelModelGrader.labels.map { it.toMutableList() } - model = evalLabelModelGrader.model - name = evalLabelModelGrader.name - passingLabels = evalLabelModelGrader.passingLabels.map { it.toMutableList() } - type = evalLabelModelGrader.type - additionalProperties = evalLabelModelGrader.additionalProperties.toMutableMap() + internal fun from(labelModelGrader: LabelModelGrader) = apply { + input = labelModelGrader.input.map { it.toMutableList() } + labels = labelModelGrader.labels.map { it.toMutableList() } + model = labelModelGrader.model + name = labelModelGrader.name + passingLabels = labelModelGrader.passingLabels.map { it.toMutableList() } + type = labelModelGrader.type + additionalProperties = labelModelGrader.additionalProperties.toMutableMap() } fun input(input: List) = input(JsonField.of(input)) @@ -328,7 +328,7 @@ private constructor( } /** - * Returns an immutable instance of [EvalLabelModelGrader]. + * Returns an immutable instance of [LabelModelGrader]. * * Further updates to this [Builder] will not mutate the returned instance. * @@ -343,8 +343,8 @@ private constructor( * * @throws IllegalStateException if any required field is unset. */ - fun build(): EvalLabelModelGrader = - EvalLabelModelGrader( + fun build(): LabelModelGrader = + LabelModelGrader( checkRequired("input", input).map { it.toImmutable() }, checkRequired("labels", labels).map { it.toImmutable() }, checkRequired("model", model), @@ -357,7 +357,7 @@ private constructor( private var validated: Boolean = false - fun validate(): EvalLabelModelGrader = apply { + fun validate(): LabelModelGrader = apply { if (validated) { return@apply } @@ -1335,7 +1335,7 @@ private constructor( return true } - return /* spotless:off */ other is EvalLabelModelGrader && input == other.input && labels == other.labels && model == other.model && name == other.name && passingLabels == other.passingLabels && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is LabelModelGrader && input == other.input && labels == other.labels && model == other.model && name == other.name && passingLabels == other.passingLabels && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ @@ -1345,5 +1345,5 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "EvalLabelModelGrader{input=$input, labels=$labels, model=$model, name=$name, passingLabels=$passingLabels, type=$type, additionalProperties=$additionalProperties}" + "LabelModelGrader{input=$input, labels=$labels, model=$model, name=$name, passingLabels=$passingLabels, type=$type, additionalProperties=$additionalProperties}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/MultiGrader.kt b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/MultiGrader.kt new file mode 100644 index 00000000..db678373 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/MultiGrader.kt @@ -0,0 +1,391 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.graders.gradermodels + +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.checkRequired +import com.openai.core.toImmutable +import com.openai.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects +import kotlin.jvm.optionals.getOrNull + +/** A MultiGrader object combines the output of multiple graders to produce a single score. */ +class MultiGrader +private constructor( + private val calculateOutput: JsonField, + private val graders: JsonField, + private val name: JsonField, + private val type: JsonValue, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("calculate_output") + @ExcludeMissing + calculateOutput: JsonField = JsonMissing.of(), + @JsonProperty("graders") @ExcludeMissing graders: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + ) : this(calculateOutput, graders, name, type, mutableMapOf()) + + /** + * A formula to calculate the output based on grader results. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun calculateOutput(): String = calculateOutput.getRequired("calculate_output") + + /** + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun graders(): Graders = graders.getRequired("graders") + + /** + * The name of the grader. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun name(): String = name.getRequired("name") + + /** + * The object type, which is always `multi`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("multi") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server responded + * with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * Returns the raw JSON value of [calculateOutput]. + * + * Unlike [calculateOutput], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("calculate_output") + @ExcludeMissing + fun _calculateOutput(): JsonField = calculateOutput + + /** + * Returns the raw JSON value of [graders]. + * + * Unlike [graders], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("graders") @ExcludeMissing fun _graders(): JsonField = graders + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [MultiGrader]. + * + * The following fields are required: + * ```java + * .calculateOutput() + * .graders() + * .name() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [MultiGrader]. */ + class Builder internal constructor() { + + private var calculateOutput: JsonField? = null + private var graders: JsonField? = null + private var name: JsonField? = null + private var type: JsonValue = JsonValue.from("multi") + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(multiGrader: MultiGrader) = apply { + calculateOutput = multiGrader.calculateOutput + graders = multiGrader.graders + name = multiGrader.name + type = multiGrader.type + additionalProperties = multiGrader.additionalProperties.toMutableMap() + } + + /** A formula to calculate the output based on grader results. */ + fun calculateOutput(calculateOutput: String) = + calculateOutput(JsonField.of(calculateOutput)) + + /** + * Sets [Builder.calculateOutput] to an arbitrary JSON value. + * + * You should usually call [Builder.calculateOutput] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun calculateOutput(calculateOutput: JsonField) = apply { + this.calculateOutput = calculateOutput + } + + fun graders(graders: Graders) = graders(JsonField.of(graders)) + + /** + * Sets [Builder.graders] to an arbitrary JSON value. + * + * You should usually call [Builder.graders] with a well-typed [Graders] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun graders(graders: JsonField) = apply { this.graders = graders } + + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("multi") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + 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) + } + + /** + * Returns an immutable instance of [MultiGrader]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .calculateOutput() + * .graders() + * .name() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): MultiGrader = + MultiGrader( + checkRequired("calculateOutput", calculateOutput), + checkRequired("graders", graders), + checkRequired("name", name), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): MultiGrader = apply { + if (validated) { + return@apply + } + + calculateOutput() + graders().validate() + name() + _type().let { + if (it != JsonValue.from("multi")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (calculateOutput.asKnown().isPresent) 1 else 0) + + (graders.asKnown().getOrNull()?.validity() ?: 0) + + (if (name.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("multi")) 1 else 0 } + + class Graders + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Graders]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Graders]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(graders: Graders) = apply { + additionalProperties = graders.additionalProperties.toMutableMap() + } + + 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) + } + + /** + * Returns an immutable instance of [Graders]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Graders = Graders(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): Graders = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Graders && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Graders{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is MultiGrader && calculateOutput == other.calculateOutput && graders == other.graders && name == other.name && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(calculateOutput, graders, name, type, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "MultiGrader{calculateOutput=$calculateOutput, graders=$graders, name=$name, type=$type, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/PythonGrader.kt b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/PythonGrader.kt new file mode 100644 index 00000000..6aef3d56 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/PythonGrader.kt @@ -0,0 +1,282 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.graders.gradermodels + +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.checkRequired +import com.openai.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects +import java.util.Optional + +/** A PythonGrader object that runs a python script on the input. */ +class PythonGrader +private constructor( + private val name: JsonField, + private val source: JsonField, + private val type: JsonValue, + private val imageTag: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("source") @ExcludeMissing source: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("image_tag") @ExcludeMissing imageTag: JsonField = JsonMissing.of(), + ) : this(name, source, type, imageTag, mutableMapOf()) + + /** + * The name of the grader. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun name(): String = name.getRequired("name") + + /** + * The source code of the python script. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun source(): String = source.getRequired("source") + + /** + * The object type, which is always `python`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("python") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server responded + * with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * The image tag to use for the python script. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun imageTag(): Optional = imageTag.getOptional("image_tag") + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source + + /** + * Returns the raw JSON value of [imageTag]. + * + * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [PythonGrader]. + * + * The following fields are required: + * ```java + * .name() + * .source() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [PythonGrader]. */ + class Builder internal constructor() { + + private var name: JsonField? = null + private var source: JsonField? = null + private var type: JsonValue = JsonValue.from("python") + private var imageTag: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(pythonGrader: PythonGrader) = apply { + name = pythonGrader.name + source = pythonGrader.source + type = pythonGrader.type + imageTag = pythonGrader.imageTag + additionalProperties = pythonGrader.additionalProperties.toMutableMap() + } + + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** The source code of the python script. */ + fun source(source: String) = source(JsonField.of(source)) + + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun source(source: JsonField) = apply { this.source = source } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("python") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** The image tag to use for the python script. */ + fun imageTag(imageTag: String) = imageTag(JsonField.of(imageTag)) + + /** + * Sets [Builder.imageTag] to an arbitrary JSON value. + * + * You should usually call [Builder.imageTag] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun imageTag(imageTag: JsonField) = apply { this.imageTag = imageTag } + + 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) + } + + /** + * Returns an immutable instance of [PythonGrader]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .name() + * .source() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): PythonGrader = + PythonGrader( + checkRequired("name", name), + checkRequired("source", source), + type, + imageTag, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): PythonGrader = apply { + if (validated) { + return@apply + } + + name() + source() + _type().let { + if (it != JsonValue.from("python")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + imageTag() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (name.asKnown().isPresent) 1 else 0) + + (if (source.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("python")) 1 else 0 } + + (if (imageTag.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is PythonGrader && name == other.name && source == other.source && type == other.type && imageTag == other.imageTag && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "PythonGrader{name=$name, source=$source, type=$type, imageTag=$imageTag, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/ScoreModelGrader.kt b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/ScoreModelGrader.kt new file mode 100644 index 00000000..b6dc3c82 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/ScoreModelGrader.kt @@ -0,0 +1,1313 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.graders.gradermodels + +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.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.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.allMaxBy +import com.openai.core.checkKnown +import com.openai.core.checkRequired +import com.openai.core.getOrThrow +import com.openai.core.toImmutable +import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.responses.ResponseInputText +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** A ScoreModelGrader object that uses a model to assign a score to the input. */ +class ScoreModelGrader +private constructor( + private val input: JsonField>, + private val model: JsonField, + private val name: JsonField, + private val type: JsonValue, + private val range: JsonField>, + private val samplingParams: JsonValue, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("input") @ExcludeMissing input: JsonField> = JsonMissing.of(), + @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("range") @ExcludeMissing range: JsonField> = JsonMissing.of(), + @JsonProperty("sampling_params") + @ExcludeMissing + samplingParams: JsonValue = JsonMissing.of(), + ) : this(input, model, name, type, range, samplingParams, mutableMapOf()) + + /** + * The input text. This may include template strings. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun input(): List = input.getRequired("input") + + /** + * The model to use for the evaluation. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun model(): String = model.getRequired("model") + + /** + * The name of the grader. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun name(): String = name.getRequired("name") + + /** + * The object type, which is always `score_model`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("score_model") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server responded + * with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * The range of the score. Defaults to `[0, 1]`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun range(): Optional> = range.getOptional("range") + + /** The sampling parameters for the model. */ + @JsonProperty("sampling_params") + @ExcludeMissing + fun _samplingParams(): JsonValue = samplingParams + + /** + * Returns the raw JSON value of [input]. + * + * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("input") @ExcludeMissing fun _input(): JsonField> = input + + /** + * Returns the raw JSON value of [model]. + * + * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + /** + * Returns the raw JSON value of [range]. + * + * Unlike [range], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("range") @ExcludeMissing fun _range(): JsonField> = range + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ScoreModelGrader]. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ScoreModelGrader]. */ + class Builder internal constructor() { + + private var input: JsonField>? = null + private var model: JsonField? = null + private var name: JsonField? = null + private var type: JsonValue = JsonValue.from("score_model") + private var range: JsonField>? = null + private var samplingParams: JsonValue = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(scoreModelGrader: ScoreModelGrader) = apply { + input = scoreModelGrader.input.map { it.toMutableList() } + model = scoreModelGrader.model + name = scoreModelGrader.name + type = scoreModelGrader.type + range = scoreModelGrader.range.map { it.toMutableList() } + samplingParams = scoreModelGrader.samplingParams + additionalProperties = scoreModelGrader.additionalProperties.toMutableMap() + } + + /** The input text. This may include template strings. */ + fun input(input: List) = input(JsonField.of(input)) + + /** + * Sets [Builder.input] to an arbitrary JSON value. + * + * You should usually call [Builder.input] with a well-typed `List` value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun input(input: JsonField>) = apply { + this.input = input.map { it.toMutableList() } + } + + /** + * Adds a single [Input] to [Builder.input]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addInput(input: Input) = apply { + this.input = + (this.input ?: JsonField.of(mutableListOf())).also { + checkKnown("input", it).add(input) + } + } + + /** The model to use for the evaluation. */ + fun model(model: String) = model(JsonField.of(model)) + + /** + * Sets [Builder.model] to an arbitrary JSON value. + * + * You should usually call [Builder.model] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun model(model: JsonField) = apply { this.model = model } + + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("score_model") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** The range of the score. Defaults to `[0, 1]`. */ + fun range(range: List) = range(JsonField.of(range)) + + /** + * Sets [Builder.range] to an arbitrary JSON value. + * + * You should usually call [Builder.range] with a well-typed `List` value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun range(range: JsonField>) = apply { + this.range = range.map { it.toMutableList() } + } + + /** + * Adds a single [Double] to [Builder.range]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addRange(range: Double) = apply { + this.range = + (this.range ?: JsonField.of(mutableListOf())).also { + checkKnown("range", it).add(range) + } + } + + /** The sampling parameters for the model. */ + fun samplingParams(samplingParams: JsonValue) = apply { + this.samplingParams = samplingParams + } + + 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) + } + + /** + * Returns an immutable instance of [ScoreModelGrader]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ScoreModelGrader = + ScoreModelGrader( + checkRequired("input", input).map { it.toImmutable() }, + checkRequired("model", model), + checkRequired("name", name), + type, + (range ?: JsonMissing.of()).map { it.toImmutable() }, + samplingParams, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ScoreModelGrader = apply { + if (validated) { + return@apply + } + + input().forEach { it.validate() } + model() + name() + _type().let { + if (it != JsonValue.from("score_model")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + range() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (input.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (model.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("score_model")) 1 else 0 } + + (range.asKnown().getOrNull()?.size ?: 0) + + /** + * A message input to the model with a role indicating instruction following hierarchy. + * Instructions given with the `developer` or `system` role take precedence over instructions + * given with the `user` role. Messages with the `assistant` role are presumed to have been + * generated by the model in previous interactions. + */ + class Input + private constructor( + private val content: JsonField, + private val role: JsonField, + private val type: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("content") @ExcludeMissing content: JsonField = JsonMissing.of(), + @JsonProperty("role") @ExcludeMissing role: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + ) : this(content, role, type, mutableMapOf()) + + /** + * Text inputs to the model - can contain template strings. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun content(): Content = content.getRequired("content") + + /** + * The role of the message input. One of `user`, `assistant`, `system`, or `developer`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun role(): Role = role.getRequired("role") + + /** + * The type of the message input. Always `message`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * Returns the raw JSON value of [content]. + * + * Unlike [content], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("content") @ExcludeMissing fun _content(): JsonField = content + + /** + * Returns the raw JSON value of [role]. + * + * Unlike [role], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("role") @ExcludeMissing fun _role(): JsonField = role + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Input]. + * + * The following fields are required: + * ```java + * .content() + * .role() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Input]. */ + class Builder internal constructor() { + + private var content: JsonField? = null + private var role: JsonField? = null + private var type: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(input: Input) = apply { + content = input.content + role = input.role + type = input.type + additionalProperties = input.additionalProperties.toMutableMap() + } + + /** Text inputs to the model - can contain template strings. */ + fun content(content: Content) = content(JsonField.of(content)) + + /** + * Sets [Builder.content] to an arbitrary JSON value. + * + * You should usually call [Builder.content] with a well-typed [Content] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun content(content: JsonField) = apply { this.content = content } + + /** Alias for calling [content] with `Content.ofTextInput(textInput)`. */ + fun content(textInput: String) = content(Content.ofTextInput(textInput)) + + /** + * Alias for calling [content] with `Content.ofResponseInputText(responseInputText)`. + */ + fun content(responseInputText: ResponseInputText) = + content(Content.ofResponseInputText(responseInputText)) + + /** Alias for calling [content] with `Content.ofOutputText(outputText)`. */ + fun content(outputText: Content.OutputText) = content(Content.ofOutputText(outputText)) + + /** + * The role of the message input. One of `user`, `assistant`, `system`, or `developer`. + */ + fun role(role: Role) = role(JsonField.of(role)) + + /** + * Sets [Builder.role] to an arbitrary JSON value. + * + * You should usually call [Builder.role] with a well-typed [Role] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun role(role: JsonField) = apply { this.role = role } + + /** The type of the message input. Always `message`. */ + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + 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) + } + + /** + * Returns an immutable instance of [Input]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .content() + * .role() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Input = + Input( + checkRequired("content", content), + checkRequired("role", role), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Input = apply { + if (validated) { + return@apply + } + + content().validate() + role().validate() + type().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (content.asKnown().getOrNull()?.validity() ?: 0) + + (role.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + /** Text inputs to the model - can contain template strings. */ + @JsonDeserialize(using = Content.Deserializer::class) + @JsonSerialize(using = Content.Serializer::class) + class Content + private constructor( + private val textInput: String? = null, + private val responseInputText: ResponseInputText? = null, + private val outputText: OutputText? = null, + private val _json: JsonValue? = null, + ) { + + /** A text input to the model. */ + fun textInput(): Optional = Optional.ofNullable(textInput) + + /** A text input to the model. */ + fun responseInputText(): Optional = + Optional.ofNullable(responseInputText) + + /** A text output from the model. */ + fun outputText(): Optional = Optional.ofNullable(outputText) + + fun isTextInput(): Boolean = textInput != null + + fun isResponseInputText(): Boolean = responseInputText != null + + fun isOutputText(): Boolean = outputText != null + + /** A text input to the model. */ + fun asTextInput(): String = textInput.getOrThrow("textInput") + + /** A text input to the model. */ + fun asResponseInputText(): ResponseInputText = + responseInputText.getOrThrow("responseInputText") + + /** A text output from the model. */ + fun asOutputText(): OutputText = outputText.getOrThrow("outputText") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + textInput != null -> visitor.visitTextInput(textInput) + responseInputText != null -> visitor.visitResponseInputText(responseInputText) + outputText != null -> visitor.visitOutputText(outputText) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Content = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitTextInput(textInput: String) {} + + override fun visitResponseInputText(responseInputText: ResponseInputText) { + responseInputText.validate() + } + + override fun visitOutputText(outputText: OutputText) { + outputText.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitTextInput(textInput: String) = 1 + + override fun visitResponseInputText(responseInputText: ResponseInputText) = + responseInputText.validity() + + override fun visitOutputText(outputText: OutputText) = outputText.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Content && textInput == other.textInput && responseInputText == other.responseInputText && outputText == other.outputText /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(textInput, responseInputText, outputText) /* spotless:on */ + + override fun toString(): String = + when { + textInput != null -> "Content{textInput=$textInput}" + responseInputText != null -> "Content{responseInputText=$responseInputText}" + outputText != null -> "Content{outputText=$outputText}" + _json != null -> "Content{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Content") + } + + companion object { + + /** A text input to the model. */ + @JvmStatic fun ofTextInput(textInput: String) = Content(textInput = textInput) + + /** A text input to the model. */ + @JvmStatic + fun ofResponseInputText(responseInputText: ResponseInputText) = + Content(responseInputText = responseInputText) + + /** A text output from the model. */ + @JvmStatic + fun ofOutputText(outputText: OutputText) = Content(outputText = outputText) + } + + /** + * An interface that defines how to map each variant of [Content] to a value of type + * [T]. + */ + interface Visitor { + + /** A text input to the model. */ + fun visitTextInput(textInput: String): T + + /** A text input to the model. */ + fun visitResponseInputText(responseInputText: ResponseInputText): T + + /** A text output from the model. */ + fun visitOutputText(outputText: OutputText): T + + /** + * Maps an unknown variant of [Content] to a value of type [T]. + * + * An instance of [Content] 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 Content: $json") + } + } + + internal class Deserializer : BaseDeserializer(Content::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Content { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Content(responseInputText = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Content(outputText = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Content(textInput = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible + // with all the possible variants (e.g. deserializing from array). + 0 -> Content(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the + // first completely valid match, or simply the first match if none are + // completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Content::class) { + + override fun serialize( + value: Content, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.textInput != null -> generator.writeObject(value.textInput) + value.responseInputText != null -> + generator.writeObject(value.responseInputText) + value.outputText != null -> generator.writeObject(value.outputText) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Content") + } + } + } + + /** A text output from the model. */ + class OutputText + private constructor( + private val text: JsonField, + private val type: JsonValue, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("text") + @ExcludeMissing + text: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + ) : this(text, type, mutableMapOf()) + + /** + * The text output from the model. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun text(): String = text.getRequired("text") + + /** + * The type of the output text. Always `output_text`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("output_text") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * Returns the raw JSON value of [text]. + * + * Unlike [text], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [OutputText]. + * + * The following fields are required: + * ```java + * .text() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [OutputText]. */ + class Builder internal constructor() { + + private var text: JsonField? = null + private var type: JsonValue = JsonValue.from("output_text") + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(outputText: OutputText) = apply { + text = outputText.text + type = outputText.type + additionalProperties = outputText.additionalProperties.toMutableMap() + } + + /** The text output from the model. */ + fun text(text: String) = text(JsonField.of(text)) + + /** + * Sets [Builder.text] to an arbitrary JSON value. + * + * You should usually call [Builder.text] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun text(text: JsonField) = apply { this.text = text } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to + * the following: + * ```java + * JsonValue.from("output_text") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + 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) + } + + /** + * Returns an immutable instance of [OutputText]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .text() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): OutputText = + OutputText( + checkRequired("text", text), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): OutputText = apply { + if (validated) { + return@apply + } + + text() + _type().let { + if (it != JsonValue.from("output_text")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (text.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("output_text")) 1 else 0 } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is OutputText && text == other.text && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(text, type, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "OutputText{text=$text, type=$type, additionalProperties=$additionalProperties}" + } + } + + /** The role of the message input. One of `user`, `assistant`, `system`, or `developer`. */ + class Role @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 USER = of("user") + + @JvmField val ASSISTANT = of("assistant") + + @JvmField val SYSTEM = of("system") + + @JvmField val DEVELOPER = of("developer") + + @JvmStatic fun of(value: String) = Role(JsonField.of(value)) + } + + /** An enum containing [Role]'s known values. */ + enum class Known { + USER, + ASSISTANT, + SYSTEM, + DEVELOPER, + } + + /** + * An enum containing [Role]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Role] 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 { + USER, + ASSISTANT, + SYSTEM, + DEVELOPER, + /** An enum member indicating that [Role] 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) { + USER -> Value.USER + ASSISTANT -> Value.ASSISTANT + SYSTEM -> Value.SYSTEM + DEVELOPER -> Value.DEVELOPER + 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) { + USER -> Known.USER + ASSISTANT -> Known.ASSISTANT + SYSTEM -> Known.SYSTEM + DEVELOPER -> Known.DEVELOPER + else -> throw OpenAIInvalidDataException("Unknown Role: $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") + } + + private var validated: Boolean = false + + fun validate(): Role = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Role && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** The type of the message input. Always `message`. */ + class Type @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 MESSAGE = of("message") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + MESSAGE + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] 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 { + MESSAGE, + /** An enum member indicating that [Type] 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) { + MESSAGE -> Value.MESSAGE + 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) { + MESSAGE -> Known.MESSAGE + else -> throw OpenAIInvalidDataException("Unknown Type: $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") + } + + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Type && 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 Input && content == other.content && role == other.role && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(content, role, type, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Input{content=$content, role=$role, type=$type, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is ScoreModelGrader && input == other.input && model == other.model && name == other.name && type == other.type && range == other.range && samplingParams == other.samplingParams && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(input, model, name, type, range, samplingParams, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ScoreModelGrader{input=$input, model=$model, name=$name, type=$type, range=$range, samplingParams=$samplingParams, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalStringCheckGrader.kt b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/StringCheckGrader.kt similarity index 92% rename from openai-java-core/src/main/kotlin/com/openai/models/evals/EvalStringCheckGrader.kt rename to openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/StringCheckGrader.kt index e1279a14..34bf9216 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalStringCheckGrader.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/StringCheckGrader.kt @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. -package com.openai.models.evals +package com.openai.models.graders.gradermodels import com.fasterxml.jackson.annotation.JsonAnyGetter import com.fasterxml.jackson.annotation.JsonAnySetter @@ -21,7 +21,7 @@ import kotlin.jvm.optionals.getOrNull * A StringCheckGrader object that performs a string comparison between input and reference using a * specified operation. */ -class EvalStringCheckGrader +class StringCheckGrader private constructor( private val input: JsonField, private val name: JsonField, @@ -130,7 +130,7 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [EvalStringCheckGrader]. + * Returns a mutable builder for constructing an instance of [StringCheckGrader]. * * The following fields are required: * ```java @@ -143,7 +143,7 @@ private constructor( @JvmStatic fun builder() = Builder() } - /** A builder for [EvalStringCheckGrader]. */ + /** A builder for [StringCheckGrader]. */ class Builder internal constructor() { private var input: JsonField? = null @@ -154,13 +154,13 @@ private constructor( private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(evalStringCheckGrader: EvalStringCheckGrader) = apply { - input = evalStringCheckGrader.input - name = evalStringCheckGrader.name - operation = evalStringCheckGrader.operation - reference = evalStringCheckGrader.reference - type = evalStringCheckGrader.type - additionalProperties = evalStringCheckGrader.additionalProperties.toMutableMap() + internal fun from(stringCheckGrader: StringCheckGrader) = apply { + input = stringCheckGrader.input + name = stringCheckGrader.name + operation = stringCheckGrader.operation + reference = stringCheckGrader.reference + type = stringCheckGrader.type + additionalProperties = stringCheckGrader.additionalProperties.toMutableMap() } /** The input text. This may include template strings. */ @@ -243,7 +243,7 @@ private constructor( } /** - * Returns an immutable instance of [EvalStringCheckGrader]. + * Returns an immutable instance of [StringCheckGrader]. * * Further updates to this [Builder] will not mutate the returned instance. * @@ -257,8 +257,8 @@ private constructor( * * @throws IllegalStateException if any required field is unset. */ - fun build(): EvalStringCheckGrader = - EvalStringCheckGrader( + fun build(): StringCheckGrader = + StringCheckGrader( checkRequired("input", input), checkRequired("name", name), checkRequired("operation", operation), @@ -270,7 +270,7 @@ private constructor( private var validated: Boolean = false - fun validate(): EvalStringCheckGrader = apply { + fun validate(): StringCheckGrader = apply { if (validated) { return@apply } @@ -453,7 +453,7 @@ private constructor( return true } - return /* spotless:off */ other is EvalStringCheckGrader && input == other.input && name == other.name && operation == other.operation && reference == other.reference && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is StringCheckGrader && input == other.input && name == other.name && operation == other.operation && reference == other.reference && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ @@ -463,5 +463,5 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "EvalStringCheckGrader{input=$input, name=$name, operation=$operation, reference=$reference, type=$type, additionalProperties=$additionalProperties}" + "StringCheckGrader{input=$input, name=$name, operation=$operation, reference=$reference, type=$type, additionalProperties=$additionalProperties}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalTextSimilarityGrader.kt b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/TextSimilarityGrader.kt similarity index 82% rename from openai-java-core/src/main/kotlin/com/openai/models/evals/EvalTextSimilarityGrader.kt rename to openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/TextSimilarityGrader.kt index 271cc2c2..a72392d4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalTextSimilarityGrader.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/TextSimilarityGrader.kt @@ -1,6 +1,6 @@ // File generated from our OpenAPI spec by Stainless. -package com.openai.models.evals +package com.openai.models.graders.gradermodels import com.fasterxml.jackson.annotation.JsonAnyGetter import com.fasterxml.jackson.annotation.JsonAnySetter @@ -15,18 +15,16 @@ import com.openai.core.checkRequired import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects -import java.util.Optional import kotlin.jvm.optionals.getOrNull /** A TextSimilarityGrader object which grades text based on similarity metrics. */ -class EvalTextSimilarityGrader +class TextSimilarityGrader private constructor( private val evaluationMetric: JsonField, private val input: JsonField, - private val passThreshold: JsonField, + private val name: JsonField, private val reference: JsonField, private val type: JsonValue, - private val name: JsonField, private val additionalProperties: MutableMap, ) { @@ -36,13 +34,10 @@ private constructor( @ExcludeMissing evaluationMetric: JsonField = JsonMissing.of(), @JsonProperty("input") @ExcludeMissing input: JsonField = JsonMissing.of(), - @JsonProperty("pass_threshold") - @ExcludeMissing - passThreshold: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), @JsonProperty("reference") @ExcludeMissing reference: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), - ) : this(evaluationMetric, input, passThreshold, reference, type, name, mutableMapOf()) + ) : this(evaluationMetric, input, name, reference, type, mutableMapOf()) /** * The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, @@ -62,12 +57,12 @@ private constructor( fun input(): String = input.getRequired("input") /** - * A float score where a value greater than or equal indicates a passing grade. + * The name of the grader. * * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - fun passThreshold(): Double = passThreshold.getRequired("pass_threshold") + fun name(): String = name.getRequired("name") /** * The text being graded against. @@ -90,14 +85,6 @@ private constructor( */ @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type - /** - * The name of the grader. - * - * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected value). - */ - fun name(): Optional = name.getOptional("name") - /** * Returns the raw JSON value of [evaluationMetric]. * @@ -116,13 +103,11 @@ private constructor( @JsonProperty("input") @ExcludeMissing fun _input(): JsonField = input /** - * Returns the raw JSON value of [passThreshold]. + * Returns the raw JSON value of [name]. * - * Unlike [passThreshold], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("pass_threshold") - @ExcludeMissing - fun _passThreshold(): JsonField = passThreshold + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name /** * Returns the raw JSON value of [reference]. @@ -131,13 +116,6 @@ private constructor( */ @JsonProperty("reference") @ExcludeMissing fun _reference(): JsonField = reference - /** - * Returns the raw JSON value of [name]. - * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name - @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { additionalProperties.put(key, value) @@ -153,39 +131,37 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [EvalTextSimilarityGrader]. + * Returns a mutable builder for constructing an instance of [TextSimilarityGrader]. * * The following fields are required: * ```java * .evaluationMetric() * .input() - * .passThreshold() + * .name() * .reference() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [EvalTextSimilarityGrader]. */ + /** A builder for [TextSimilarityGrader]. */ class Builder internal constructor() { private var evaluationMetric: JsonField? = null private var input: JsonField? = null - private var passThreshold: JsonField? = null + private var name: JsonField? = null private var reference: JsonField? = null private var type: JsonValue = JsonValue.from("text_similarity") - private var name: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(evalTextSimilarityGrader: EvalTextSimilarityGrader) = apply { - evaluationMetric = evalTextSimilarityGrader.evaluationMetric - input = evalTextSimilarityGrader.input - passThreshold = evalTextSimilarityGrader.passThreshold - reference = evalTextSimilarityGrader.reference - type = evalTextSimilarityGrader.type - name = evalTextSimilarityGrader.name - additionalProperties = evalTextSimilarityGrader.additionalProperties.toMutableMap() + internal fun from(textSimilarityGrader: TextSimilarityGrader) = apply { + evaluationMetric = textSimilarityGrader.evaluationMetric + input = textSimilarityGrader.input + name = textSimilarityGrader.name + reference = textSimilarityGrader.reference + type = textSimilarityGrader.type + additionalProperties = textSimilarityGrader.additionalProperties.toMutableMap() } /** @@ -217,19 +193,16 @@ private constructor( */ fun input(input: JsonField) = apply { this.input = input } - /** A float score where a value greater than or equal indicates a passing grade. */ - fun passThreshold(passThreshold: Double) = passThreshold(JsonField.of(passThreshold)) + /** The name of the grader. */ + fun name(name: String) = name(JsonField.of(name)) /** - * Sets [Builder.passThreshold] to an arbitrary JSON value. + * Sets [Builder.name] to an arbitrary JSON value. * - * You should usually call [Builder.passThreshold] with a well-typed [Double] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun passThreshold(passThreshold: JsonField) = apply { - this.passThreshold = passThreshold - } + fun name(name: JsonField) = apply { this.name = name } /** The text being graded against. */ fun reference(reference: String) = reference(JsonField.of(reference)) @@ -257,17 +230,6 @@ private constructor( */ fun type(type: JsonValue) = apply { this.type = type } - /** The name of the grader. */ - fun name(name: String) = name(JsonField.of(name)) - - /** - * Sets [Builder.name] to an arbitrary JSON value. - * - * You should usually call [Builder.name] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun name(name: JsonField) = apply { this.name = name } - fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() putAllAdditionalProperties(additionalProperties) @@ -288,7 +250,7 @@ private constructor( } /** - * Returns an immutable instance of [EvalTextSimilarityGrader]. + * Returns an immutable instance of [TextSimilarityGrader]. * * Further updates to this [Builder] will not mutate the returned instance. * @@ -296,41 +258,39 @@ private constructor( * ```java * .evaluationMetric() * .input() - * .passThreshold() + * .name() * .reference() * ``` * * @throws IllegalStateException if any required field is unset. */ - fun build(): EvalTextSimilarityGrader = - EvalTextSimilarityGrader( + fun build(): TextSimilarityGrader = + TextSimilarityGrader( checkRequired("evaluationMetric", evaluationMetric), checkRequired("input", input), - checkRequired("passThreshold", passThreshold), + checkRequired("name", name), checkRequired("reference", reference), type, - name, additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): EvalTextSimilarityGrader = apply { + fun validate(): TextSimilarityGrader = apply { if (validated) { return@apply } evaluationMetric().validate() input() - passThreshold() + name() reference() _type().let { if (it != JsonValue.from("text_similarity")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - name() validated = true } @@ -351,10 +311,9 @@ private constructor( internal fun validity(): Int = (evaluationMetric.asKnown().getOrNull()?.validity() ?: 0) + (if (input.asKnown().isPresent) 1 else 0) + - (if (passThreshold.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + (if (reference.asKnown().isPresent) 1 else 0) + - type.let { if (it == JsonValue.from("text_similarity")) 1 else 0 } + - (if (name.asKnown().isPresent) 1 else 0) + type.let { if (it == JsonValue.from("text_similarity")) 1 else 0 } /** * The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, @@ -542,15 +501,15 @@ private constructor( return true } - return /* spotless:off */ other is EvalTextSimilarityGrader && evaluationMetric == other.evaluationMetric && input == other.input && passThreshold == other.passThreshold && reference == other.reference && type == other.type && name == other.name && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is TextSimilarityGrader && evaluationMetric == other.evaluationMetric && input == other.input && name == other.name && reference == other.reference && type == other.type && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(evaluationMetric, input, passThreshold, reference, type, name, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(evaluationMetric, input, name, reference, type, additionalProperties) } /* spotless:on */ override fun hashCode(): Int = hashCode override fun toString() = - "EvalTextSimilarityGrader{evaluationMetric=$evaluationMetric, input=$input, passThreshold=$passThreshold, reference=$reference, type=$type, name=$name, additionalProperties=$additionalProperties}" + "TextSimilarityGrader{evaluationMetric=$evaluationMetric, input=$input, name=$name, reference=$reference, type=$type, additionalProperties=$additionalProperties}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/models/ModelDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/models/ModelDeleteParams.kt index f87f9a5a..c3b48aed 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/models/ModelDeleteParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/models/ModelDeleteParams.kt @@ -4,25 +4,25 @@ package com.openai.models.models import com.openai.core.JsonValue 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 +import kotlin.jvm.optionals.getOrNull /** * Delete a fine-tuned model. You must have the Owner role in your organization to delete a model. */ class ModelDeleteParams private constructor( - private val model: String, + private val model: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, ) : Params { - fun model(): String = model + fun model(): Optional = Optional.ofNullable(model) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -34,14 +34,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [ModelDeleteParams]. - * - * The following fields are required: - * ```java - * .model() - * ``` - */ + @JvmStatic fun none(): ModelDeleteParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [ModelDeleteParams]. */ @JvmStatic fun builder() = Builder() } @@ -61,7 +56,10 @@ private constructor( additionalBodyProperties = modelDeleteParams.additionalBodyProperties.toMutableMap() } - fun model(model: String) = apply { this.model = model } + fun model(model: String?) = apply { this.model = model } + + /** Alias for calling [Builder.model] with `model.orElse(null)`. */ + fun model(model: Optional) = model(model.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -187,17 +185,10 @@ private constructor( * Returns an immutable instance of [ModelDeleteParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .model() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): ModelDeleteParams = ModelDeleteParams( - checkRequired("model", model), + model, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -209,7 +200,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> model + 0 -> model ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/models/ModelListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/models/ModelListPage.kt index 2da80544..9ce4bde9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/models/ModelListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/models/ModelListPage.kt @@ -2,13 +2,12 @@ package com.openai.models.models +import com.openai.core.AutoPager import com.openai.core.JsonValue +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.ModelService import java.util.Objects -import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [ModelService.list] */ @@ -17,7 +16,7 @@ private constructor( private val service: ModelService, private val params: ModelListParams, private val response: ModelListPageResponse, -) { +) : Page { /** * Delegates to [ModelListPageResponse], but gracefully handles missing data. @@ -29,13 +28,16 @@ private constructor( /** @see [ModelListPageResponse.object_] */ fun object_(): JsonValue = response._object_() - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional = Optional.empty() + override fun hasNextPage(): Boolean = items().isNotEmpty() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + fun nextPageParams(): ModelListParams = + throw IllegalStateException("Cannot construct next page params") - fun autoPager(): AutoPager = AutoPager(this) + override fun nextPage(): ModelListPage = service.list(nextPageParams()) + + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): ModelListParams = params @@ -104,25 +106,6 @@ private constructor( ) } - class AutoPager(private val firstPage: ModelListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/models/ModelListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/models/ModelListPageAsync.kt index 653a08f5..335ac20c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/models/ModelListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/models/ModelListPageAsync.kt @@ -2,23 +2,24 @@ package com.openai.models.models +import com.openai.core.AutoPagerAsync import com.openai.core.JsonValue +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.ModelServiceAsync import java.util.Objects -import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [ModelServiceAsync.list] */ class ModelListPageAsync private constructor( private val service: ModelServiceAsync, + private val streamHandlerExecutor: Executor, private val params: ModelListParams, private val response: ModelListPageResponse, -) { +) : PageAsync { /** * Delegates to [ModelListPageResponse], but gracefully handles missing data. @@ -30,16 +31,16 @@ private constructor( /** @see [ModelListPageResponse.object_] */ fun object_(): JsonValue = response._object_() - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional = Optional.empty() + override fun hasNextPage(): Boolean = items().isNotEmpty() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + fun nextPageParams(): ModelListParams = + throw IllegalStateException("Cannot construct next page params") - fun autoPager(): AutoPager = AutoPager(this) + override fun nextPage(): CompletableFuture = service.list(nextPageParams()) + + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): ModelListParams = params @@ -57,6 +58,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -68,18 +70,24 @@ private constructor( class Builder internal constructor() { private var service: ModelServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: ModelListParams? = null private var response: ModelListPageResponse? = null @JvmSynthetic internal fun from(modelListPageAsync: ModelListPageAsync) = apply { service = modelListPageAsync.service + streamHandlerExecutor = modelListPageAsync.streamHandlerExecutor params = modelListPageAsync.params response = modelListPageAsync.response } fun service(service: ModelServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: ModelListParams) = apply { this.params = params } @@ -94,6 +102,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -103,47 +112,22 @@ private constructor( fun build(): ModelListPageAsync = ModelListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: ModelListPageAsync) { - - fun forEach(action: Predicate, executor: Executor): CompletableFuture { - fun CompletableFuture>.forEach( - action: (Model) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is ModelListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is ModelListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "ModelListPageAsync{service=$service, params=$params, response=$response}" + "ModelListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/models/ModelRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/models/ModelRetrieveParams.kt index d76cdabc..1be64185 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/models/ModelRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/models/ModelRetrieveParams.kt @@ -3,10 +3,11 @@ package com.openai.models.models import com.openai.core.Params -import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Retrieves a model instance, providing basic information about the model such as the owner and @@ -14,12 +15,12 @@ import java.util.Objects */ class ModelRetrieveParams private constructor( - private val model: String, + private val model: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun model(): String = model + fun model(): Optional = Optional.ofNullable(model) fun _additionalHeaders(): Headers = additionalHeaders @@ -29,14 +30,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [ModelRetrieveParams]. - * - * The following fields are required: - * ```java - * .model() - * ``` - */ + @JvmStatic fun none(): ModelRetrieveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [ModelRetrieveParams]. */ @JvmStatic fun builder() = Builder() } @@ -54,7 +50,10 @@ private constructor( additionalQueryParams = modelRetrieveParams.additionalQueryParams.toBuilder() } - fun model(model: String) = apply { this.model = model } + fun model(model: String?) = apply { this.model = model } + + /** Alias for calling [Builder.model] with `model.orElse(null)`. */ + fun model(model: Optional) = model(model.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -158,25 +157,14 @@ private constructor( * Returns an immutable instance of [ModelRetrieveParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .model() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): ModelRetrieveParams = - ModelRetrieveParams( - checkRequired("model", model), - additionalHeaders.build(), - additionalQueryParams.build(), - ) + ModelRetrieveParams(model, additionalHeaders.build(), additionalQueryParams.build()) } fun _pathParam(index: Int): String = when (index) { - 0 -> model + 0 -> model ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreateParams.kt index 6b512ed9..345f6a93 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCreateParams.kt @@ -19,6 +19,7 @@ import com.openai.core.Enum import com.openai.core.ExcludeMissing import com.openai.core.JsonField import com.openai.core.JsonMissing +import com.openai.core.JsonSchemaLocalValidation import com.openai.core.JsonValue import com.openai.core.Params import com.openai.core.allMaxBy @@ -784,6 +785,28 @@ private constructor( */ fun text(text: JsonField) = apply { body.text(text) } + /** + * Sets the text configuration's format to a JSON schema derived from the structure of the + * given class. This changes the builder to a type-safe + * [StructuredResponseCreateParams.Builder] that will build a + * [StructuredResponseCreateParams] instance when `build()` is called. + * + * @param responseType A class from which a JSON schema will be derived to define the text + * configuration's format. + * @param localValidation [JsonSchemaLocalValidation.YES] (the default) to validate the JSON + * schema locally when it is generated by this method to confirm that it adheres to the + * requirements and restrictions on JSON schemas imposed by the OpenAI specification; or + * [JsonSchemaLocalValidation.NO] to skip local validation and rely only on remote + * validation. See the SDK documentation for more details. + * @throws IllegalArgumentException If local validation is enabled, but it fails because a + * valid JSON schema cannot be derived from the given class. + */ + @JvmOverloads + fun text( + responseType: Class, + localValidation: JsonSchemaLocalValidation = JsonSchemaLocalValidation.YES, + ) = StructuredResponseCreateParams.builder().wrap(responseType, this, localValidation) + /** * How the model should select which tool (or tools) to use when generating a response. See * the `tools` parameter to see how to specify which tools the model can call. diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseDeleteParams.kt index b3fae35d..38398d98 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseDeleteParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseDeleteParams.kt @@ -4,23 +4,23 @@ package com.openai.models.responses import com.openai.core.JsonValue 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 +import kotlin.jvm.optionals.getOrNull /** Deletes a model response with the given ID. */ class ResponseDeleteParams private constructor( - private val responseId: String, + private val responseId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, ) : Params { - fun responseId(): String = responseId + fun responseId(): Optional = Optional.ofNullable(responseId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -32,14 +32,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [ResponseDeleteParams]. - * - * The following fields are required: - * ```java - * .responseId() - * ``` - */ + @JvmStatic fun none(): ResponseDeleteParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [ResponseDeleteParams]. */ @JvmStatic fun builder() = Builder() } @@ -59,7 +54,10 @@ private constructor( additionalBodyProperties = responseDeleteParams.additionalBodyProperties.toMutableMap() } - fun responseId(responseId: String) = apply { this.responseId = responseId } + fun responseId(responseId: String?) = apply { this.responseId = responseId } + + /** Alias for calling [Builder.responseId] with `responseId.orElse(null)`. */ + fun responseId(responseId: Optional) = responseId(responseId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -185,17 +183,10 @@ private constructor( * Returns an immutable instance of [ResponseDeleteParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .responseId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): ResponseDeleteParams = ResponseDeleteParams( - checkRequired("responseId", responseId), + responseId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -207,7 +198,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> responseId + 0 -> responseId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchToolCall.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchToolCall.kt index 35019c26..1e0389b6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchToolCall.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchToolCall.kt @@ -497,7 +497,7 @@ private constructor( private val attributes: JsonField, private val fileId: JsonField, private val filename: JsonField, - private val score: JsonField, + private val score: JsonField, private val text: JsonField, private val additionalProperties: MutableMap, ) { @@ -511,7 +511,7 @@ private constructor( @JsonProperty("filename") @ExcludeMissing filename: JsonField = JsonMissing.of(), - @JsonProperty("score") @ExcludeMissing score: JsonField = JsonMissing.of(), + @JsonProperty("score") @ExcludeMissing score: JsonField = JsonMissing.of(), @JsonProperty("text") @ExcludeMissing text: JsonField = JsonMissing.of(), ) : this(attributes, fileId, filename, score, text, mutableMapOf()) @@ -549,7 +549,7 @@ private constructor( * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ - fun score(): Optional = score.getOptional("score") + fun score(): Optional = score.getOptional("score") /** * The text that was retrieved from the file. @@ -587,7 +587,7 @@ private constructor( * * Unlike [score], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("score") @ExcludeMissing fun _score(): JsonField = score + @JsonProperty("score") @ExcludeMissing fun _score(): JsonField = score /** * Returns the raw JSON value of [text]. @@ -620,7 +620,7 @@ private constructor( private var attributes: JsonField = JsonMissing.of() private var fileId: JsonField = JsonMissing.of() private var filename: JsonField = JsonMissing.of() - private var score: JsonField = JsonMissing.of() + private var score: JsonField = JsonMissing.of() private var text: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -682,16 +682,16 @@ private constructor( fun filename(filename: JsonField) = apply { this.filename = filename } /** The relevance score of the file - a value between 0 and 1. */ - fun score(score: Double) = score(JsonField.of(score)) + fun score(score: Float) = score(JsonField.of(score)) /** * Sets [Builder.score] to an arbitrary JSON value. * - * You should usually call [Builder.score] with a well-typed [Double] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. + * You should usually call [Builder.score] with a well-typed [Float] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun score(score: JsonField) = apply { this.score = score } + fun score(score: JsonField) = apply { this.score = score } /** The text that was retrieved from the file. */ fun text(text: String) = text(JsonField.of(text)) diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseRetrieveParams.kt index 3c73de87..bc158949 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseRetrieveParams.kt @@ -3,7 +3,6 @@ package com.openai.models.responses 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 @@ -14,13 +13,13 @@ import kotlin.jvm.optionals.getOrNull /** Retrieves a model response with the given ID. */ class ResponseRetrieveParams private constructor( - private val responseId: String, + private val responseId: String?, private val include: List?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun responseId(): String = responseId + fun responseId(): Optional = Optional.ofNullable(responseId) /** * Additional fields to include in the response. See the `include` parameter for Response @@ -36,14 +35,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [ResponseRetrieveParams]. - * - * The following fields are required: - * ```java - * .responseId() - * ``` - */ + @JvmStatic fun none(): ResponseRetrieveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [ResponseRetrieveParams]. */ @JvmStatic fun builder() = Builder() } @@ -63,7 +57,10 @@ private constructor( additionalQueryParams = responseRetrieveParams.additionalQueryParams.toBuilder() } - fun responseId(responseId: String) = apply { this.responseId = responseId } + fun responseId(responseId: String?) = apply { this.responseId = responseId } + + /** Alias for calling [Builder.responseId] with `responseId.orElse(null)`. */ + fun responseId(responseId: Optional) = responseId(responseId.getOrNull()) /** * Additional fields to include in the response. See the `include` parameter for Response @@ -187,17 +184,10 @@ private constructor( * Returns an immutable instance of [ResponseRetrieveParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .responseId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): ResponseRetrieveParams = ResponseRetrieveParams( - checkRequired("responseId", responseId), + responseId, include?.toImmutable(), additionalHeaders.build(), additionalQueryParams.build(), @@ -206,7 +196,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> responseId + 0 -> responseId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponse.kt new file mode 100644 index 00000000..f28865b0 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponse.kt @@ -0,0 +1,197 @@ +package com.openai.models.responses + +import com.openai.core.JsonField +import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException +import com.openai.models.Reasoning +import com.openai.models.ResponsesModel +import java.util.Objects +import java.util.Optional + +/** + * A wrapper for [Response] that provides type-safe access to the [output] when using the + * _Structured Outputs_ feature to deserialize a JSON response to an instance of an arbitrary class. + * See the SDK documentation for more details on _Structured Outputs_. + * + * @param T The type of the class to which the JSON data in the response will be deserialized. + */ +class StructuredResponse( + @get:JvmName("responseType") val responseType: Class, + @get:JvmName("rawResponse") val rawResponse: Response, +) { + /** @see Response.id */ + fun id(): String = rawResponse.id() + + /** @see Response.createdAt */ + fun createdAt(): Double = rawResponse.createdAt() + + /** @see Response.error */ + fun error(): Optional = rawResponse.error() + + /** @see Response.incompleteDetails */ + fun incompleteDetails(): Optional = rawResponse.incompleteDetails() + + /** @see Response.instructions */ + fun instructions(): Optional = rawResponse.instructions() + + /** @see Response.metadata */ + fun metadata(): Optional = rawResponse.metadata() + + /** @see Response.model */ + fun model(): ResponsesModel = rawResponse.model() + + /** @see Response._object_ */ + fun _object_(): JsonValue = rawResponse._object_() + + private val output by lazy { + rawResponse._output().map { outputs -> + outputs.map { StructuredResponseOutputItem(responseType, it) } + } + } + + /** @see Response.output */ + fun output(): List> = output.getRequired("output") + + /** @see Response.parallelToolCalls */ + fun parallelToolCalls(): Boolean = rawResponse.parallelToolCalls() + + /** @see Response.temperature */ + fun temperature(): Optional = rawResponse.temperature() + + /** @see Response.toolChoice */ + fun toolChoice(): Response.ToolChoice = rawResponse.toolChoice() + + /** @see Response.tools */ + fun tools(): List = rawResponse.tools() + + /** @see Response.topP */ + fun topP(): Optional = rawResponse.topP() + + /** @see Response.maxOutputTokens */ + fun maxOutputTokens(): Optional = rawResponse.maxOutputTokens() + + /** @see Response.previousResponseId */ + fun previousResponseId(): Optional = rawResponse.previousResponseId() + + /** @see Response.reasoning */ + fun reasoning(): Optional = rawResponse.reasoning() + + /** @see Response.serviceTier */ + fun serviceTier(): Optional = rawResponse.serviceTier() + + /** @see Response.status */ + fun status(): Optional = rawResponse.status() + + /** @see Response.text */ + fun text(): Optional = rawResponse.text() + + /** @see Response.truncation */ + fun truncation(): Optional = rawResponse.truncation() + + /** @see Response.usage */ + fun usage(): Optional = rawResponse.usage() + + /** @see Response.user */ + fun user(): Optional = rawResponse.user() + + /** @see Response._id */ + fun _id(): JsonField = rawResponse._id() + + /** @see Response._createdAt */ + fun _createdAt(): JsonField = rawResponse._createdAt() + + /** @see Response._error */ + fun _error(): JsonField = rawResponse._error() + + /** @see Response._incompleteDetails */ + fun _incompleteDetails(): JsonField = + rawResponse._incompleteDetails() + + /** @see Response._instructions */ + fun _instructions(): JsonField = rawResponse._instructions() + + /** @see Response._metadata */ + fun _metadata(): JsonField = rawResponse._metadata() + + /** @see Response._model */ + fun _model(): JsonField = rawResponse._model() + + /** @see Response._output */ + fun _output(): JsonField>> = output + + /** @see Response._parallelToolCalls */ + fun _parallelToolCalls(): JsonField = rawResponse._parallelToolCalls() + + /** @see Response._temperature */ + fun _temperature(): JsonField = rawResponse._temperature() + + /** @see Response._toolChoice */ + fun _toolChoice(): JsonField = rawResponse._toolChoice() + + /** @see Response._tools */ + fun _tools(): JsonField> = rawResponse._tools() + + /** @see Response._topP */ + fun _topP(): JsonField = rawResponse._topP() + + /** @see Response._maxOutputTokens */ + fun _maxOutputTokens(): JsonField = rawResponse._maxOutputTokens() + + /** @see Response._previousResponseId */ + fun _previousResponseId(): JsonField = rawResponse._previousResponseId() + + /** @see Response._reasoning */ + fun _reasoning(): JsonField = rawResponse._reasoning() + + /** @see Response._serviceTier */ + fun _serviceTier(): JsonField = rawResponse._serviceTier() + + /** @see Response._status */ + fun _status(): JsonField = rawResponse._status() + + /** @see Response._text */ + fun _text(): JsonField = rawResponse._text() + + /** @see Response._truncation */ + fun _truncation(): JsonField = rawResponse._truncation() + + /** @see Response._usage */ + fun _usage(): JsonField = rawResponse._usage() + + /** @see Response._user */ + fun _user(): JsonField = rawResponse._user() + + /** @see Response._additionalProperties */ + fun _additionalProperties(): Map = rawResponse._additionalProperties() + + /** @see Response.validate */ + fun validate(): StructuredResponse = apply { + output().forEach { it.validate() } + rawResponse.validate() + } + + /** @see Response.isValid */ + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + return other is StructuredResponse<*> && + responseType == other.responseType && + rawResponse == other.rawResponse + } + + private val hashCode: Int by lazy { Objects.hash(responseType, rawResponse) } + + override fun hashCode(): Int = hashCode + + override fun toString(): String = + "${javaClass.simpleName}{responseType=$responseType, rawResponse=$rawResponse}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseCreateParams.kt new file mode 100644 index 00000000..aae191ac --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseCreateParams.kt @@ -0,0 +1,526 @@ +package com.openai.models.responses + +import com.openai.core.JsonField +import com.openai.core.JsonSchemaLocalValidation +import com.openai.core.JsonValue +import com.openai.core.checkRequired +import com.openai.core.http.Headers +import com.openai.core.http.QueryParams +import com.openai.core.textConfigFromClass +import com.openai.models.ChatModel +import com.openai.models.Reasoning +import com.openai.models.ResponsesModel +import java.util.Objects +import java.util.Optional + +/** + * A wrapper for [ResponseCreateParams] that provides a type-safe [Builder] that can record the + * [responseType] used to derive a JSON schema from an arbitrary class when using the _Structured + * Outputs_ feature. When a JSON response is received, it is deserialized to am instance of that + * type. See the SDK documentation for more details on _Structured Outputs_. + * + * @param T The type of the class that will be used to derive the JSON schema in the request and to + * which the JSON response will be deserialized. + */ +class StructuredResponseCreateParams( + @get:JvmName("responseType") val responseType: Class, + /** + * The raw, underlying response create parameters wrapped by this structured instance of the + * parameters. + */ + @get:JvmName("rawParams") val rawParams: ResponseCreateParams, +) { + + companion object { + /** @see ResponseCreateParams.builder */ + @JvmStatic fun builder() = Builder() + } + + class Builder internal constructor() { + private var responseType: Class? = null + private var paramsBuilder = ResponseCreateParams.builder() + + @JvmSynthetic + internal fun wrap( + responseType: Class, + paramsBuilder: ResponseCreateParams.Builder, + localValidation: JsonSchemaLocalValidation, + ) = apply { + this.responseType = responseType + this.paramsBuilder = paramsBuilder + text(responseType, localValidation) + } + + /** Injects a given `ResponseCreateParams.Builder`. For use only when testing. */ + @JvmSynthetic + internal fun inject(paramsBuilder: ResponseCreateParams.Builder) = apply { + this.paramsBuilder = paramsBuilder + } + + // The `body(...)` function is deliberately not supported. + + /** @see ResponseCreateParams.Builder.input */ + fun input(input: ResponseCreateParams.Input) = apply { paramsBuilder.input(input) } + + /** @see ResponseCreateParams.Builder.input */ + fun input(input: JsonField) = apply { + paramsBuilder.input(input) + } + + /** @see ResponseCreateParams.Builder.input */ + fun input(text: String) = apply { paramsBuilder.input(text) } + + /** @see ResponseCreateParams.Builder.inputOfResponse */ + fun inputOfResponse(response: List) = apply { + paramsBuilder.inputOfResponse(response) + } + + /** @see ResponseCreateParams.Builder.model */ + fun model(model: ResponsesModel) = apply { paramsBuilder.model(model) } + + /** @see ResponseCreateParams.Builder.model */ + fun model(model: JsonField) = apply { paramsBuilder.model(model) } + + /** @see ResponseCreateParams.Builder.model */ + fun model(string: String) = apply { paramsBuilder.model(string) } + + /** @see ResponseCreateParams.Builder.model */ + fun model(chat: ChatModel) = apply { paramsBuilder.model(chat) } + + /** @see ResponseCreateParams.Builder.model */ + fun model(only: ResponsesModel.ResponsesOnlyModel) = apply { paramsBuilder.model(only) } + + /** @see ResponseCreateParams.Builder.include */ + fun include(include: List?) = apply { paramsBuilder.include(include) } + + /** @see ResponseCreateParams.Builder.include */ + fun include(include: Optional>) = apply { + paramsBuilder.include(include) + } + + /** @see ResponseCreateParams.Builder.include */ + fun include(include: JsonField>) = apply { + paramsBuilder.include(include) + } + + /** @see ResponseCreateParams.Builder.addInclude */ + fun addInclude(include: ResponseIncludable) = apply { paramsBuilder.addInclude(include) } + + /** @see ResponseCreateParams.Builder.instructions */ + fun instructions(instructions: String?) = apply { paramsBuilder.instructions(instructions) } + + /** @see ResponseCreateParams.Builder.instructions */ + fun instructions(instructions: Optional) = apply { + paramsBuilder.instructions(instructions) + } + + /** @see ResponseCreateParams.Builder.instructions */ + fun instructions(instructions: JsonField) = apply { + paramsBuilder.instructions(instructions) + } + + /** @see ResponseCreateParams.Builder.maxOutputTokens */ + fun maxOutputTokens(maxOutputTokens: Long?) = apply { + paramsBuilder.maxOutputTokens(maxOutputTokens) + } + + /** @see ResponseCreateParams.Builder.maxOutputTokens */ + fun maxOutputTokens(maxOutputTokens: Long) = apply { + paramsBuilder.maxOutputTokens(maxOutputTokens) + } + + /** @see ResponseCreateParams.Builder.maxOutputTokens */ + fun maxOutputTokens(maxOutputTokens: Optional) = apply { + paramsBuilder.maxOutputTokens(maxOutputTokens) + } + + /** @see ResponseCreateParams.Builder.maxOutputTokens */ + fun maxOutputTokens(maxOutputTokens: JsonField) = apply { + paramsBuilder.maxOutputTokens(maxOutputTokens) + } + + /** @see ResponseCreateParams.Builder.metadata */ + fun metadata(metadata: ResponseCreateParams.Metadata?) = apply { + paramsBuilder.metadata(metadata) + } + + /** @see ResponseCreateParams.Builder.metadata */ + fun metadata(metadata: Optional) = apply { + paramsBuilder.metadata(metadata) + } + + /** @see ResponseCreateParams.Builder.metadata */ + fun metadata(metadata: JsonField) = apply { + paramsBuilder.metadata(metadata) + } + + /** @see ResponseCreateParams.Builder.parallelToolCalls */ + fun parallelToolCalls(parallelToolCalls: Boolean?) = apply { + paramsBuilder.parallelToolCalls(parallelToolCalls) + } + + /** @see ResponseCreateParams.Builder.parallelToolCalls */ + fun parallelToolCalls(parallelToolCalls: Boolean) = apply { + paramsBuilder.parallelToolCalls(parallelToolCalls) + } + + /** @see ResponseCreateParams.Builder.parallelToolCalls */ + fun parallelToolCalls(parallelToolCalls: Optional) = apply { + paramsBuilder.parallelToolCalls(parallelToolCalls) + } + + /** @see ResponseCreateParams.Builder.parallelToolCalls */ + fun parallelToolCalls(parallelToolCalls: JsonField) = apply { + paramsBuilder.parallelToolCalls(parallelToolCalls) + } + + /** @see ResponseCreateParams.Builder.previousResponseId */ + fun previousResponseId(previousResponseId: String?) = apply { + paramsBuilder.previousResponseId(previousResponseId) + } + + /** @see ResponseCreateParams.Builder.previousResponseId */ + fun previousResponseId(previousResponseId: Optional) = apply { + paramsBuilder.previousResponseId(previousResponseId) + } + + /** @see ResponseCreateParams.Builder.previousResponseId */ + fun previousResponseId(previousResponseId: JsonField) = apply { + paramsBuilder.previousResponseId(previousResponseId) + } + + /** @see ResponseCreateParams.Builder.reasoning */ + fun reasoning(reasoning: Reasoning?) = apply { paramsBuilder.reasoning(reasoning) } + + /** @see ResponseCreateParams.Builder.reasoning */ + fun reasoning(reasoning: Optional) = apply { paramsBuilder.reasoning(reasoning) } + + /** @see ResponseCreateParams.Builder.reasoning */ + fun reasoning(reasoning: JsonField) = apply { + paramsBuilder.reasoning(reasoning) + } + + /** @see ResponseCreateParams.Builder.serviceTier */ + fun serviceTier(serviceTier: ResponseCreateParams.ServiceTier?) = apply { + paramsBuilder.serviceTier(serviceTier) + } + + /** @see ResponseCreateParams.Builder.serviceTier */ + fun serviceTier(serviceTier: Optional) = apply { + paramsBuilder.serviceTier(serviceTier) + } + + /** @see ResponseCreateParams.Builder.serviceTier */ + fun serviceTier(serviceTier: JsonField) = apply { + paramsBuilder.serviceTier(serviceTier) + } + + /** @see ResponseCreateParams.Builder.store */ + fun store(store: Boolean?) = apply { paramsBuilder.store(store) } + + /** @see ResponseCreateParams.Builder.store */ + fun store(store: Boolean) = apply { paramsBuilder.store(store) } + + /** @see ResponseCreateParams.Builder.store */ + fun store(store: Optional) = apply { paramsBuilder.store(store) } + + /** @see ResponseCreateParams.Builder.store */ + fun store(store: JsonField) = apply { paramsBuilder.store(store) } + + /** @see ResponseCreateParams.Builder.temperature */ + fun temperature(temperature: Double?) = apply { paramsBuilder.temperature(temperature) } + + /** @see ResponseCreateParams.Builder.temperature */ + fun temperature(temperature: Double) = apply { paramsBuilder.temperature(temperature) } + + /** @see ResponseCreateParams.Builder.temperature */ + fun temperature(temperature: Optional) = apply { + paramsBuilder.temperature(temperature) + } + + /** @see ResponseCreateParams.Builder.temperature */ + fun temperature(temperature: JsonField) = apply { + paramsBuilder.temperature(temperature) + } + + /** + * Sets the text configuration's format to a JSON schema derived from the structure of the + * given class. + * + * @see ResponseCreateParams.Builder.text + */ + @JvmOverloads + fun text( + responseType: Class, + localValidation: JsonSchemaLocalValidation = JsonSchemaLocalValidation.YES, + ) = apply { + this.responseType = responseType + paramsBuilder.text(textConfigFromClass(responseType, localValidation)) + } + + /** @see ResponseCreateParams.Builder.toolChoice */ + fun toolChoice(toolChoice: ResponseCreateParams.ToolChoice) = apply { + paramsBuilder.toolChoice(toolChoice) + } + + /** @see ResponseCreateParams.Builder.toolChoice */ + fun toolChoice(toolChoice: JsonField) = apply { + paramsBuilder.toolChoice(toolChoice) + } + + /** @see ResponseCreateParams.Builder.toolChoice */ + fun toolChoice(options: ToolChoiceOptions) = apply { paramsBuilder.toolChoice(options) } + + /** @see ResponseCreateParams.Builder.toolChoice */ + fun toolChoice(types: ToolChoiceTypes) = apply { paramsBuilder.toolChoice(types) } + + /** @see ResponseCreateParams.Builder.toolChoice */ + fun toolChoice(function: ToolChoiceFunction) = apply { paramsBuilder.toolChoice(function) } + + /** @see ResponseCreateParams.Builder.tools */ + fun tools(tools: List) = apply { paramsBuilder.tools(tools) } + + /** @see ResponseCreateParams.Builder.tools */ + fun tools(tools: JsonField>) = apply { paramsBuilder.tools(tools) } + + /** @see ResponseCreateParams.Builder.addTool */ + fun addTool(tool: Tool) = apply { paramsBuilder.addTool(tool) } + + /** @see ResponseCreateParams.Builder.addTool */ + fun addTool(fileSearch: FileSearchTool) = apply { paramsBuilder.addTool(fileSearch) } + + /** @see ResponseCreateParams.Builder.addFileSearchTool */ + fun addFileSearchTool(vectorStoreIds: List) = apply { + paramsBuilder.addFileSearchTool(vectorStoreIds) + } + + /** @see ResponseCreateParams.Builder.addTool */ + fun addTool(function: FunctionTool) = apply { paramsBuilder.addTool(function) } + + /** @see ResponseCreateParams.Builder.addTool */ + fun addTool(webSearch: WebSearchTool) = apply { paramsBuilder.addTool(webSearch) } + + /** @see ResponseCreateParams.Builder.addTool */ + fun addTool(computerUsePreview: ComputerTool) = apply { + paramsBuilder.addTool(computerUsePreview) + } + + /** @see ResponseCreateParams.Builder.topP */ + fun topP(topP: Double?) = apply { paramsBuilder.topP(topP) } + + /** @see ResponseCreateParams.Builder.topP */ + fun topP(topP: Double) = apply { paramsBuilder.topP(topP) } + + /** @see ResponseCreateParams.Builder.topP */ + fun topP(topP: Optional) = apply { paramsBuilder.topP(topP) } + + /** @see ResponseCreateParams.Builder.topP */ + fun topP(topP: JsonField) = apply { paramsBuilder.topP(topP) } + + /** @see ResponseCreateParams.Builder.truncation */ + fun truncation(truncation: ResponseCreateParams.Truncation?) = apply { + paramsBuilder.truncation(truncation) + } + + /** @see ResponseCreateParams.Builder.truncation */ + fun truncation(truncation: Optional) = apply { + paramsBuilder.truncation(truncation) + } + + /** @see ResponseCreateParams.Builder.truncation */ + fun truncation(truncation: JsonField) = apply { + paramsBuilder.truncation(truncation) + } + + /** @see ResponseCreateParams.Builder.user */ + fun user(user: String) = apply { paramsBuilder.user(user) } + + /** @see ResponseCreateParams.Builder.user */ + fun user(user: JsonField) = apply { paramsBuilder.user(user) } + + /** @see ResponseCreateParams.Builder.additionalBodyProperties */ + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + paramsBuilder.additionalBodyProperties(additionalBodyProperties) + } + + /** @see ResponseCreateParams.Builder.putAdditionalBodyProperty */ + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + paramsBuilder.putAdditionalBodyProperty(key, value) + } + + /** @see ResponseCreateParams.Builder.putAllAdditionalBodyProperties */ + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + paramsBuilder.putAllAdditionalBodyProperties(additionalBodyProperties) + } + + /** @see ResponseCreateParams.Builder.removeAdditionalBodyProperty */ + fun removeAdditionalBodyProperty(key: String) = apply { + paramsBuilder.removeAdditionalBodyProperty(key) + } + + /** @see ResponseCreateParams.Builder.removeAllAdditionalBodyProperties */ + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + paramsBuilder.removeAllAdditionalBodyProperties(keys) + } + + /** @see ResponseCreateParams.Builder.additionalHeaders */ + fun additionalHeaders(additionalHeaders: Headers) = apply { + paramsBuilder.additionalHeaders(additionalHeaders) + } + + /** @see ResponseCreateParams.Builder.additionalHeaders */ + fun additionalHeaders(additionalHeaders: Map>) = apply { + paramsBuilder.additionalHeaders(additionalHeaders) + } + + /** @see ResponseCreateParams.Builder.putAdditionalHeader */ + fun putAdditionalHeader(name: String, value: String) = apply { + paramsBuilder.putAdditionalHeader(name, value) + } + + /** @see ResponseCreateParams.Builder.putAdditionalHeaders */ + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + paramsBuilder.putAdditionalHeaders(name, values) + } + + /** @see ResponseCreateParams.Builder.putAllAdditionalHeaders */ + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + paramsBuilder.putAllAdditionalHeaders(additionalHeaders) + } + + /** @see ResponseCreateParams.Builder.putAllAdditionalHeaders */ + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + paramsBuilder.putAllAdditionalHeaders(additionalHeaders) + } + + /** @see ResponseCreateParams.Builder.replaceAdditionalHeaders */ + fun replaceAdditionalHeaders(name: String, value: String) = apply { + paramsBuilder.replaceAdditionalHeaders(name, value) + } + + /** @see ResponseCreateParams.Builder.replaceAdditionalHeaders */ + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + paramsBuilder.replaceAdditionalHeaders(name, values) + } + + /** @see ResponseCreateParams.Builder.replaceAllAdditionalHeaders */ + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + paramsBuilder.replaceAllAdditionalHeaders(additionalHeaders) + } + + /** @see ResponseCreateParams.Builder.replaceAllAdditionalHeaders */ + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + paramsBuilder.replaceAllAdditionalHeaders(additionalHeaders) + } + + /** @see ResponseCreateParams.Builder.removeAdditionalHeaders */ + fun removeAdditionalHeaders(name: String) = apply { + paramsBuilder.removeAdditionalHeaders(name) + } + + /** @see ResponseCreateParams.Builder.removeAllAdditionalHeaders */ + fun removeAllAdditionalHeaders(names: Set) = apply { + paramsBuilder.removeAllAdditionalHeaders(names) + } + + /** @see ResponseCreateParams.Builder.additionalQueryParams */ + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + paramsBuilder.additionalQueryParams(additionalQueryParams) + } + + /** @see ResponseCreateParams.Builder.additionalQueryParams */ + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + paramsBuilder.additionalQueryParams(additionalQueryParams) + } + + /** @see ResponseCreateParams.Builder.putAdditionalQueryParam */ + fun putAdditionalQueryParam(key: String, value: String) = apply { + paramsBuilder.putAdditionalQueryParam(key, value) + } + + /** @see ResponseCreateParams.Builder.putAdditionalQueryParams */ + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + paramsBuilder.putAdditionalQueryParams(key, values) + } + + /** @see ResponseCreateParams.Builder.putAllAdditionalQueryParams */ + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + paramsBuilder.putAllAdditionalQueryParams(additionalQueryParams) + } + + /** @see ResponseCreateParams.Builder.putAllAdditionalQueryParams */ + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + paramsBuilder.putAllAdditionalQueryParams(additionalQueryParams) + } + + /** @see ResponseCreateParams.Builder.replaceAdditionalQueryParams */ + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + paramsBuilder.replaceAdditionalQueryParams(key, value) + } + + /** @see ResponseCreateParams.Builder.replaceAdditionalQueryParams */ + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + paramsBuilder.replaceAdditionalQueryParams(key, values) + } + + /** @see ResponseCreateParams.Builder.replaceAllAdditionalQueryParams */ + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + paramsBuilder.replaceAllAdditionalQueryParams(additionalQueryParams) + } + + /** @see ResponseCreateParams.Builder.replaceAllAdditionalQueryParams */ + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + paramsBuilder.replaceAllAdditionalQueryParams(additionalQueryParams) + } + + /** @see ResponseCreateParams.Builder.removeAdditionalQueryParams */ + fun removeAdditionalQueryParams(key: String) = apply { + paramsBuilder.removeAdditionalQueryParams(key) + } + + /** @see ResponseCreateParams.Builder.removeAllAdditionalQueryParams */ + fun removeAllAdditionalQueryParams(keys: Set) = apply { + paramsBuilder.removeAllAdditionalQueryParams(keys) + } + + /** + * Returns an immutable instance of [ResponseCreateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .text() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build() = + StructuredResponseCreateParams( + checkRequired("responseType", responseType), + paramsBuilder.build(), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is StructuredResponseCreateParams<*> && + responseType == other.responseType && + rawParams == other.rawParams + } + + private val hashCode: Int by lazy { Objects.hash(responseType, rawParams) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "${javaClass.simpleName}{responseType=$responseType, rawParams=$rawParams}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseOutputItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseOutputItem.kt new file mode 100644 index 00000000..39cd6b6b --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseOutputItem.kt @@ -0,0 +1,189 @@ +package com.openai.models.responses + +import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrElse +import kotlin.jvm.optionals.getOrNull + +/** + * A wrapper for [ResponseOutputItem] that provides type-safe access to the [message] when using the + * _Structured Outputs_ feature to deserialize a JSON response to an instance of an arbitrary class. + * See the SDK documentation for more details on _Structured Outputs_. + * + * @param T The type of the class to which the JSON data in the content will be deserialized when + * [message] is called. + */ +class StructuredResponseOutputItem( + @get:JvmName("responseType") val responseType: Class, + @get:JvmName("rawOutputItem") val rawOutputItem: ResponseOutputItem, +) { + private val message by lazy { + rawOutputItem.message().map { StructuredResponseOutputMessage(responseType, it) } + } + + /** @see ResponseOutputItem.message */ + fun message(): Optional> = message + + /** @see ResponseOutputItem.fileSearchCall */ + fun fileSearchCall(): Optional = rawOutputItem.fileSearchCall() + + /** @see ResponseOutputItem.functionCall */ + fun functionCall(): Optional = rawOutputItem.functionCall() + + /** @see ResponseOutputItem.webSearchCall */ + fun webSearchCall(): Optional = rawOutputItem.webSearchCall() + + /** @see ResponseOutputItem.computerCall */ + fun computerCall(): Optional = rawOutputItem.computerCall() + + /** @see ResponseOutputItem.reasoning */ + fun reasoning(): Optional = rawOutputItem.reasoning() + + /** @see ResponseOutputItem.isMessage */ + fun isMessage(): Boolean = message().isPresent + + /** @see ResponseOutputItem.isFileSearchCall */ + fun isFileSearchCall(): Boolean = rawOutputItem.isFileSearchCall() + + /** @see ResponseOutputItem.isFunctionCall */ + fun isFunctionCall(): Boolean = rawOutputItem.isFunctionCall() + + /** @see ResponseOutputItem.isWebSearchCall */ + fun isWebSearchCall(): Boolean = rawOutputItem.isWebSearchCall() + + /** @see ResponseOutputItem.isComputerCall */ + fun isComputerCall(): Boolean = rawOutputItem.isComputerCall() + + /** @see ResponseOutputItem.isReasoning */ + fun isReasoning(): Boolean = rawOutputItem.isReasoning() + + /** @see ResponseOutputItem.asMessage */ + fun asMessage(): StructuredResponseOutputMessage = + message.getOrElse { + // Same behavior as `com.openai.core.getOrThrow` used by the delegate class. + throw OpenAIInvalidDataException("`message` is not present") + } + + /** @see ResponseOutputItem.asFileSearchCall */ + fun asFileSearchCall(): ResponseFileSearchToolCall = rawOutputItem.asFileSearchCall() + + /** @see ResponseOutputItem.asFunctionCall */ + fun asFunctionCall(): ResponseFunctionToolCall = rawOutputItem.asFunctionCall() + + /** @see ResponseOutputItem.asWebSearchCall */ + fun asWebSearchCall(): ResponseFunctionWebSearch = rawOutputItem.asWebSearchCall() + + /** @see ResponseOutputItem.asComputerCall */ + fun asComputerCall(): ResponseComputerToolCall = rawOutputItem.asComputerCall() + + /** @see ResponseOutputItem.asReasoning */ + fun asReasoning(): ResponseReasoningItem = rawOutputItem.asReasoning() + + /** @see ResponseOutputItem._json */ + fun _json(): Optional = rawOutputItem._json() + + /** @see ResponseOutputItem.accept */ + fun accept(visitor: Visitor): R = + when { + isMessage() -> visitor.visitMessage(asMessage()) + isFileSearchCall() -> visitor.visitFileSearchCall(asFileSearchCall()) + isFunctionCall() -> visitor.visitFunctionCall(asFunctionCall()) + isWebSearchCall() -> visitor.visitWebSearchCall(asWebSearchCall()) + isComputerCall() -> visitor.visitComputerCall(asComputerCall()) + isReasoning() -> visitor.visitReasoning(asReasoning()) + else -> visitor.unknown(_json().getOrNull()) + } + + private var validated: Boolean = false + + /** @see ResponseOutputItem.validate */ + fun validate(): StructuredResponseOutputItem = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitMessage(message: StructuredResponseOutputMessage) { + message.validate() + } + + override fun visitFileSearchCall(fileSearchCall: ResponseFileSearchToolCall) { + fileSearchCall.validate() + } + + override fun visitFunctionCall(functionCall: ResponseFunctionToolCall) { + functionCall.validate() + } + + override fun visitWebSearchCall(webSearchCall: ResponseFunctionWebSearch) { + webSearchCall.validate() + } + + override fun visitComputerCall(computerCall: ResponseComputerToolCall) { + computerCall.validate() + } + + override fun visitReasoning(reasoning: ResponseReasoningItem) { + reasoning.validate() + } + } + ) + validated = true + } + + /** @see ResponseOutputItem.isValid */ + fun isValid(): Boolean = + try { + validate() + true + } catch (_: OpenAIInvalidDataException) { + false + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is StructuredResponseOutputItem<*> && + responseType == other.responseType && + rawOutputItem == other.rawOutputItem + } + + override fun hashCode(): Int = Objects.hash(responseType, rawOutputItem) + + override fun toString(): String = + "${javaClass.simpleName}{responseType=$responseType, rawOutputItem=$rawOutputItem}" + + /** @see ResponseOutputItem.Visitor */ + // In keeping with the delegate's `Visitor`, `T` is used to refer to the return type of each + // function. `R` (for "Response") is used to refer to the response type, which is otherwise + // named `T` in the outer class, but confusion here is probably preferable to confusion there. + interface Visitor { + /** @see ResponseOutputItem.Visitor.visitMessage */ + fun visitMessage(message: StructuredResponseOutputMessage): T + + /** @see ResponseOutputItem.Visitor.visitFileSearchCall */ + fun visitFileSearchCall(fileSearchCall: ResponseFileSearchToolCall): T + + /** @see ResponseOutputItem.Visitor.visitFunctionCall */ + fun visitFunctionCall(functionCall: ResponseFunctionToolCall): T + + /** @see ResponseOutputItem.Visitor.visitWebSearchCall */ + fun visitWebSearchCall(webSearchCall: ResponseFunctionWebSearch): T + + /** @see ResponseOutputItem.Visitor.visitComputerCall */ + fun visitComputerCall(computerCall: ResponseComputerToolCall): T + + /** @see ResponseOutputItem.Visitor.visitReasoning */ + fun visitReasoning(reasoning: ResponseReasoningItem): T + + /** @see ResponseOutputItem.Visitor.unknown */ + fun unknown(json: JsonValue?): T { + throw OpenAIInvalidDataException("Unknown ResponseOutputItem: $json") + } + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseOutputMessage.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseOutputMessage.kt new file mode 100644 index 00000000..b7083a25 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseOutputMessage.kt @@ -0,0 +1,181 @@ +package com.openai.models.responses + +import com.openai.core.JsonField +import com.openai.core.JsonValue +import com.openai.core.responseTypeFromJson +import com.openai.errors.OpenAIInvalidDataException +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrElse +import kotlin.jvm.optionals.getOrNull + +/** + * A wrapper for [ResponseOutputMessage] that provides type-safe access to the [content] when using + * the _Structured Outputs_ feature to deserialize a JSON response to an instance of an arbitrary + * class. See the SDK documentation for more details on _Structured Outputs_. + * + * @param T The type of the class to which the JSON data in the content will be deserialized when + * the output text of the [content] is retrieved. + */ +class StructuredResponseOutputMessage( + @get:JvmName("responseType") val responseType: Class, + @get:JvmName("rawMessage") val rawMessage: ResponseOutputMessage, +) { + /** @see ResponseOutputMessage.id */ + fun id(): String = rawMessage.id() + + private val content by lazy { + rawMessage._content().map { contents -> contents.map { Content(responseType, it) } } + } + + /** @see ResponseOutputMessage.content */ + fun content(): List> = content.getRequired("content") + + /** @see ResponseOutputMessage._role */ + fun _role(): JsonValue = rawMessage._role() + + /** @see ResponseOutputMessage.status */ + fun status(): ResponseOutputMessage.Status = rawMessage.status() + + /** @see ResponseOutputMessage._type */ + fun _type(): JsonValue = rawMessage._type() + + /** @see ResponseOutputMessage._id */ + fun _id(): JsonField = rawMessage._id() + + /** @see ResponseOutputMessage._content */ + fun _content(): JsonField>> = content + + /** @see ResponseOutputMessage._status */ + fun _status(): JsonField = rawMessage._status() + + /** @see ResponseOutputMessage._additionalProperties */ + fun _additionalProperties(): Map = rawMessage._additionalProperties() + + /** @see ResponseOutputMessage.validate */ + fun validate(): StructuredResponseOutputMessage = apply { + // `content()` is a different type to that in the delegate class. + content().forEach { it.validate() } + rawMessage.validate() + } + + /** @see ResponseOutputMessage.isValid */ + fun isValid(): Boolean = + try { + validate() + true + } catch (_: OpenAIInvalidDataException) { + false + } + + /** @see ResponseOutputMessage.Content */ + class Content( + @get:JvmName("responseType") val responseType: Class, + @get:JvmName("rawContent") val rawContent: ResponseOutputMessage.Content, + ) { + private val outputText by lazy { + rawContent.outputText().map { responseTypeFromJson(it.text(), responseType) } + } + + /** + * Gets the output text, but deserialized to an instance of the response type class. + * + * @see ResponseOutputMessage.Content.outputText + */ + fun outputText(): Optional = outputText + + /** @see ResponseOutputMessage.Content.refusal */ + fun refusal(): Optional = rawContent.refusal() + + /** @see ResponseOutputMessage.Content.isOutputText */ + // No need to check `outputText`; the delegate can just check the source value is present. + fun isOutputText(): Boolean = rawContent.isOutputText() + + /** @see ResponseOutputMessage.Content.isRefusal */ + fun isRefusal(): Boolean = rawContent.isRefusal() + + /** @see ResponseOutputMessage.Content.asOutputText */ + fun asOutputText(): T = + outputText.getOrElse { + // Same behavior as `com.openai.core.getOrThrow` used by the delegate class. + throw OpenAIInvalidDataException("`outputText` is not present") + } + + /** @see ResponseOutputMessage.Content.asRefusal */ + fun asRefusal(): ResponseOutputRefusal = rawContent.asRefusal() + + /** @see ResponseOutputMessage.Content._json */ + fun _json(): Optional = rawContent._json() + + /** @see ResponseOutputMessage.Content.accept */ + fun accept(visitor: Visitor): R = + when { + outputText.isPresent -> visitor.visitOutputText(outputText.get()) + refusal().isPresent -> visitor.visitRefusal(refusal().get()) + else -> visitor.unknown(_json().getOrNull()) + } + + /** @see ResponseOutputMessage.Content.validate */ + fun validate(): Content = apply { + // The `outputText` object, as it is a user-defined type that is unlikely to have a + // `validate()` function/method, so validate the underlying `ResponseOutputText` from + // which it is derived. That can be done by the delegate class. + rawContent.validate() + } + + /** @see ResponseOutputMessage.Content.isValid */ + fun isValid(): Boolean = + try { + validate() + true + } catch (_: OpenAIInvalidDataException) { + false + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Content<*> && + rawContent == other.rawContent && + responseType == other.responseType + } + + override fun hashCode(): Int = Objects.hash(rawContent, responseType) + + override fun toString(): String = + "${javaClass.simpleName}{responseType=$responseType, rawContent=$rawContent}" + + /** @see ResponseOutputMessage.Content.Visitor */ + interface Visitor { + /** @see ResponseOutputMessage.Content.Visitor.visitOutputText */ + fun visitOutputText(outputText: T): R + + /** @see ResponseOutputMessage.Content.Visitor.visitRefusal */ + fun visitRefusal(refusal: ResponseOutputRefusal): R + + /** @see ResponseOutputMessage.Content.Visitor.unknown */ + fun unknown(json: JsonValue?): R { + throw OpenAIInvalidDataException("Unknown Content: $json") + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is StructuredResponseOutputMessage<*> && + responseType == other.responseType && + rawMessage == other.rawMessage + } + + private val hashCode: Int by lazy { Objects.hash(responseType, rawMessage) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "${javaClass.simpleName}{responseType=$responseType, rawMessage=$rawMessage}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPage.kt index 1fbb02ef..6218c55b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPage.kt @@ -2,6 +2,8 @@ package com.openai.models.responses.inputitems +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.models.responses.ResponseComputerToolCall import com.openai.models.responses.ResponseComputerToolCallOutputItem @@ -15,8 +17,6 @@ import com.openai.models.responses.ResponseOutputMessage import com.openai.services.blocking.responses.InputItemService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [InputItemService.list] */ @@ -25,7 +25,7 @@ private constructor( private val service: InputItemService, private val params: InputItemListParams, private val response: ResponseItemList, -) { +) : Page { /** * Delegates to [ResponseItemList], but gracefully handles missing data. @@ -41,63 +41,57 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() - - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } - - return Optional.of( - params - .toBuilder() - .after( - data() - .last() - .accept( - object : ResponseItem.Visitor> { - override fun visitResponseInputMessageItem( - responseInputMessageItem: ResponseInputMessageItem - ): Optional = - responseInputMessageItem._id().getOptional("id") - - override fun visitResponseOutputMessage( - responseOutputMessage: ResponseOutputMessage - ): Optional = responseOutputMessage._id().getOptional("id") - - override fun visitFileSearchCall( - fileSearchCall: ResponseFileSearchToolCall - ): Optional = fileSearchCall._id().getOptional("id") - - override fun visitComputerCall( - computerCall: ResponseComputerToolCall - ): Optional = computerCall._id().getOptional("id") - - override fun visitComputerCallOutput( - computerCallOutput: ResponseComputerToolCallOutputItem - ): Optional = computerCallOutput._id().getOptional("id") - - override fun visitWebSearchCall( - webSearchCall: ResponseFunctionWebSearch - ): Optional = webSearchCall._id().getOptional("id") - - override fun visitFunctionCall( - functionCall: ResponseFunctionToolCallItem - ): Optional = functionCall._id().getOptional("id") - - override fun visitFunctionCallOutput( - functionCallOutput: ResponseFunctionToolCallOutputItem - ): Optional = functionCallOutput._id().getOptional("id") - } - ) - ) - .build() - ) - } + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() + + fun nextPageParams(): InputItemListParams = + params + .toBuilder() + .after( + items() + .last() + .accept( + object : ResponseItem.Visitor> { + override fun visitResponseInputMessageItem( + responseInputMessageItem: ResponseInputMessageItem + ): Optional = responseInputMessageItem._id().getOptional("id") + + override fun visitResponseOutputMessage( + responseOutputMessage: ResponseOutputMessage + ): Optional = responseOutputMessage._id().getOptional("id") + + override fun visitFileSearchCall( + fileSearchCall: ResponseFileSearchToolCall + ): Optional = fileSearchCall._id().getOptional("id") + + override fun visitComputerCall( + computerCall: ResponseComputerToolCall + ): Optional = computerCall._id().getOptional("id") + + override fun visitComputerCallOutput( + computerCallOutput: ResponseComputerToolCallOutputItem + ): Optional = computerCallOutput._id().getOptional("id") + + override fun visitWebSearchCall( + webSearchCall: ResponseFunctionWebSearch + ): Optional = webSearchCall._id().getOptional("id") + + override fun visitFunctionCall( + functionCall: ResponseFunctionToolCallItem + ): Optional = functionCall._id().getOptional("id") + + override fun visitFunctionCallOutput( + functionCallOutput: ResponseFunctionToolCallOutputItem + ): Optional = functionCallOutput._id().getOptional("id") + } + ) + ) + .build() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + override fun nextPage(): InputItemListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): InputItemListParams = params @@ -166,25 +160,6 @@ private constructor( ) } - class AutoPager(private val firstPage: InputItemListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPageAsync.kt index ddde7ff4..a840b310 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPageAsync.kt @@ -2,6 +2,8 @@ package com.openai.models.responses.inputitems +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.models.responses.ResponseComputerToolCall import com.openai.models.responses.ResponseComputerToolCallOutputItem @@ -17,16 +19,16 @@ import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [InputItemServiceAsync.list] */ class InputItemListPageAsync private constructor( private val service: InputItemServiceAsync, + private val streamHandlerExecutor: Executor, private val params: InputItemListParams, private val response: ResponseItemList, -) { +) : PageAsync { /** * Delegates to [ResponseItemList], but gracefully handles missing data. @@ -42,66 +44,58 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() - - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } - - return Optional.of( - params - .toBuilder() - .after( - data() - .last() - .accept( - object : ResponseItem.Visitor> { - override fun visitResponseInputMessageItem( - responseInputMessageItem: ResponseInputMessageItem - ): Optional = - responseInputMessageItem._id().getOptional("id") - - override fun visitResponseOutputMessage( - responseOutputMessage: ResponseOutputMessage - ): Optional = responseOutputMessage._id().getOptional("id") - - override fun visitFileSearchCall( - fileSearchCall: ResponseFileSearchToolCall - ): Optional = fileSearchCall._id().getOptional("id") - - override fun visitComputerCall( - computerCall: ResponseComputerToolCall - ): Optional = computerCall._id().getOptional("id") - - override fun visitComputerCallOutput( - computerCallOutput: ResponseComputerToolCallOutputItem - ): Optional = computerCallOutput._id().getOptional("id") - - override fun visitWebSearchCall( - webSearchCall: ResponseFunctionWebSearch - ): Optional = webSearchCall._id().getOptional("id") - - override fun visitFunctionCall( - functionCall: ResponseFunctionToolCallItem - ): Optional = functionCall._id().getOptional("id") - - override fun visitFunctionCallOutput( - functionCallOutput: ResponseFunctionToolCallOutputItem - ): Optional = functionCallOutput._id().getOptional("id") - } - ) - ) - .build() - ) - } + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() + + fun nextPageParams(): InputItemListParams = + params + .toBuilder() + .after( + items() + .last() + .accept( + object : ResponseItem.Visitor> { + override fun visitResponseInputMessageItem( + responseInputMessageItem: ResponseInputMessageItem + ): Optional = responseInputMessageItem._id().getOptional("id") + + override fun visitResponseOutputMessage( + responseOutputMessage: ResponseOutputMessage + ): Optional = responseOutputMessage._id().getOptional("id") + + override fun visitFileSearchCall( + fileSearchCall: ResponseFileSearchToolCall + ): Optional = fileSearchCall._id().getOptional("id") + + override fun visitComputerCall( + computerCall: ResponseComputerToolCall + ): Optional = computerCall._id().getOptional("id") + + override fun visitComputerCallOutput( + computerCallOutput: ResponseComputerToolCallOutputItem + ): Optional = computerCallOutput._id().getOptional("id") + + override fun visitWebSearchCall( + webSearchCall: ResponseFunctionWebSearch + ): Optional = webSearchCall._id().getOptional("id") + + override fun visitFunctionCall( + functionCall: ResponseFunctionToolCallItem + ): Optional = functionCall._id().getOptional("id") + + override fun visitFunctionCallOutput( + functionCallOutput: ResponseFunctionToolCallOutputItem + ): Optional = functionCallOutput._id().getOptional("id") + } + ) + ) + .build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = + service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): InputItemListParams = params @@ -119,6 +113,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -130,18 +125,24 @@ private constructor( class Builder internal constructor() { private var service: InputItemServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: InputItemListParams? = null private var response: ResponseItemList? = null @JvmSynthetic internal fun from(inputItemListPageAsync: InputItemListPageAsync) = apply { service = inputItemListPageAsync.service + streamHandlerExecutor = inputItemListPageAsync.streamHandlerExecutor params = inputItemListPageAsync.params response = inputItemListPageAsync.response } fun service(service: InputItemServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: InputItemListParams) = apply { this.params = params } @@ -156,6 +157,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -165,47 +167,22 @@ private constructor( fun build(): InputItemListPageAsync = InputItemListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: InputItemListPageAsync) { - - fun forEach(action: Predicate, executor: Executor): CompletableFuture { - fun CompletableFuture>.forEach( - action: (ResponseItem) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is InputItemListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is InputItemListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "InputItemListPageAsync{service=$service, params=$params, response=$response}" + "InputItemListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListParams.kt index 11ddf361..e3f3cbd9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListParams.kt @@ -6,7 +6,6 @@ import com.fasterxml.jackson.annotation.JsonCreator import com.openai.core.Enum import com.openai.core.JsonField 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 @@ -19,7 +18,7 @@ import kotlin.jvm.optionals.getOrNull /** Returns a list of input items for a given response. */ class InputItemListParams private constructor( - private val responseId: String, + private val responseId: String?, private val after: String?, private val before: String?, private val include: List?, @@ -29,7 +28,7 @@ private constructor( private val additionalQueryParams: QueryParams, ) : Params { - fun responseId(): String = responseId + fun responseId(): Optional = Optional.ofNullable(responseId) /** An item ID to list items after, used in pagination. */ fun after(): Optional = Optional.ofNullable(after) @@ -64,14 +63,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [InputItemListParams]. - * - * The following fields are required: - * ```java - * .responseId() - * ``` - */ + @JvmStatic fun none(): InputItemListParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [InputItemListParams]. */ @JvmStatic fun builder() = Builder() } @@ -99,7 +93,10 @@ private constructor( additionalQueryParams = inputItemListParams.additionalQueryParams.toBuilder() } - fun responseId(responseId: String) = apply { this.responseId = responseId } + fun responseId(responseId: String?) = apply { this.responseId = responseId } + + /** Alias for calling [Builder.responseId] with `responseId.orElse(null)`. */ + fun responseId(responseId: Optional) = responseId(responseId.getOrNull()) /** An item ID to list items after, used in pagination. */ fun after(after: String?) = apply { this.after = after } @@ -261,17 +258,10 @@ private constructor( * Returns an immutable instance of [InputItemListParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .responseId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): InputItemListParams = InputItemListParams( - checkRequired("responseId", responseId), + responseId, after, before, include?.toImmutable(), @@ -284,7 +274,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> responseId + 0 -> responseId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCancelParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCancelParams.kt index 5b2fe821..013b5ddc 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCancelParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCancelParams.kt @@ -4,23 +4,23 @@ package com.openai.models.uploads import com.openai.core.JsonValue 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 +import kotlin.jvm.optionals.getOrNull /** Cancels the Upload. No Parts may be added after an Upload is cancelled. */ class UploadCancelParams private constructor( - private val uploadId: String, + private val uploadId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, ) : Params { - fun uploadId(): String = uploadId + fun uploadId(): Optional = Optional.ofNullable(uploadId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -32,14 +32,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [UploadCancelParams]. - * - * The following fields are required: - * ```java - * .uploadId() - * ``` - */ + @JvmStatic fun none(): UploadCancelParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [UploadCancelParams]. */ @JvmStatic fun builder() = Builder() } @@ -59,7 +54,10 @@ private constructor( additionalBodyProperties = uploadCancelParams.additionalBodyProperties.toMutableMap() } - fun uploadId(uploadId: String) = apply { this.uploadId = uploadId } + fun uploadId(uploadId: String?) = apply { this.uploadId = uploadId } + + /** Alias for calling [Builder.uploadId] with `uploadId.orElse(null)`. */ + fun uploadId(uploadId: Optional) = uploadId(uploadId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -185,17 +183,10 @@ private constructor( * Returns an immutable instance of [UploadCancelParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .uploadId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): UploadCancelParams = UploadCancelParams( - checkRequired("uploadId", uploadId), + uploadId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -207,7 +198,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> uploadId + 0 -> uploadId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCompleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCompleteParams.kt index 990ef1d4..fb5603c1 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCompleteParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/uploads/UploadCompleteParams.kt @@ -36,13 +36,13 @@ import kotlin.jvm.optionals.getOrNull */ class UploadCompleteParams private constructor( - private val uploadId: String, + private val uploadId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun uploadId(): String = uploadId + fun uploadId(): Optional = Optional.ofNullable(uploadId) /** * The ordered list of Part IDs. @@ -90,7 +90,6 @@ private constructor( * * The following fields are required: * ```java - * .uploadId() * .partIds() * ``` */ @@ -113,7 +112,10 @@ private constructor( additionalQueryParams = uploadCompleteParams.additionalQueryParams.toBuilder() } - fun uploadId(uploadId: String) = apply { this.uploadId = uploadId } + fun uploadId(uploadId: String?) = apply { this.uploadId = uploadId } + + /** Alias for calling [Builder.uploadId] with `uploadId.orElse(null)`. */ + fun uploadId(uploadId: Optional) = uploadId(uploadId.getOrNull()) /** * Sets the entire request body. @@ -282,7 +284,6 @@ private constructor( * * The following fields are required: * ```java - * .uploadId() * .partIds() * ``` * @@ -290,7 +291,7 @@ private constructor( */ fun build(): UploadCompleteParams = UploadCompleteParams( - checkRequired("uploadId", uploadId), + uploadId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -301,7 +302,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> uploadId + 0 -> uploadId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/PartCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/PartCreateParams.kt index a396a730..791713e4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/PartCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/uploads/parts/PartCreateParams.kt @@ -14,8 +14,10 @@ import com.openai.errors.OpenAIInvalidDataException import java.io.InputStream import java.nio.file.Path import java.util.Objects +import java.util.Optional import kotlin.io.path.inputStream import kotlin.io.path.name +import kotlin.jvm.optionals.getOrNull /** * Adds a [Part](https://platform.openai.com/docs/api-reference/uploads/part-object) to an @@ -29,13 +31,13 @@ import kotlin.io.path.name */ class PartCreateParams private constructor( - private val uploadId: String, + private val uploadId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun uploadId(): String = uploadId + fun uploadId(): Optional = Optional.ofNullable(uploadId) /** * The chunk of bytes for this Part. @@ -65,7 +67,6 @@ private constructor( * * The following fields are required: * ```java - * .uploadId() * .data() * ``` */ @@ -88,7 +89,10 @@ private constructor( additionalQueryParams = partCreateParams.additionalQueryParams.toBuilder() } - fun uploadId(uploadId: String) = apply { this.uploadId = uploadId } + fun uploadId(uploadId: String?) = apply { this.uploadId = uploadId } + + /** Alias for calling [Builder.uploadId] with `uploadId.orElse(null)`. */ + fun uploadId(uploadId: Optional) = uploadId(uploadId.getOrNull()) /** * Sets the entire request body. @@ -222,7 +226,6 @@ private constructor( * * The following fields are required: * ```java - * .uploadId() * .data() * ``` * @@ -230,7 +233,7 @@ private constructor( */ fun build(): PartCreateParams = PartCreateParams( - checkRequired("uploadId", uploadId), + uploadId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -241,7 +244,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> uploadId + 0 -> uploadId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreDeleteParams.kt index efd70d98..b6bbda6e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreDeleteParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreDeleteParams.kt @@ -4,23 +4,23 @@ package com.openai.models.vectorstores import com.openai.core.JsonValue 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 +import kotlin.jvm.optionals.getOrNull /** Delete a vector store. */ class VectorStoreDeleteParams private constructor( - private val vectorStoreId: String, + private val vectorStoreId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, ) : Params { - fun vectorStoreId(): String = vectorStoreId + fun vectorStoreId(): Optional = Optional.ofNullable(vectorStoreId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -32,14 +32,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [VectorStoreDeleteParams]. - * - * The following fields are required: - * ```java - * .vectorStoreId() - * ``` - */ + @JvmStatic fun none(): VectorStoreDeleteParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [VectorStoreDeleteParams]. */ @JvmStatic fun builder() = Builder() } @@ -60,7 +55,11 @@ private constructor( vectorStoreDeleteParams.additionalBodyProperties.toMutableMap() } - fun vectorStoreId(vectorStoreId: String) = apply { this.vectorStoreId = vectorStoreId } + fun vectorStoreId(vectorStoreId: String?) = apply { this.vectorStoreId = vectorStoreId } + + /** Alias for calling [Builder.vectorStoreId] with `vectorStoreId.orElse(null)`. */ + fun vectorStoreId(vectorStoreId: Optional) = + vectorStoreId(vectorStoreId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -186,17 +185,10 @@ private constructor( * Returns an immutable instance of [VectorStoreDeleteParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .vectorStoreId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): VectorStoreDeleteParams = VectorStoreDeleteParams( - checkRequired("vectorStoreId", vectorStoreId), + vectorStoreId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -208,7 +200,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> vectorStoreId + 0 -> vectorStoreId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPage.kt index fcb7958e..52852ddb 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPage.kt @@ -2,12 +2,12 @@ package com.openai.models.vectorstores +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.VectorStoreService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [VectorStoreService.list] */ @@ -16,7 +16,7 @@ private constructor( private val service: VectorStoreService, private val params: VectorStoreListParams, private val response: VectorStoreListPageResponse, -) { +) : Page { /** * Delegates to [VectorStoreListPageResponse], but gracefully handles missing data. @@ -32,19 +32,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): VectorStoreListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + override fun nextPage(): VectorStoreListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): VectorStoreListParams = params @@ -113,25 +110,6 @@ private constructor( ) } - class AutoPager(private val firstPage: VectorStoreListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPageAsync.kt index 1e2daf96..785cf997 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPageAsync.kt @@ -2,22 +2,24 @@ package com.openai.models.vectorstores +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.VectorStoreServiceAsync import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [VectorStoreServiceAsync.list] */ class VectorStoreListPageAsync private constructor( private val service: VectorStoreServiceAsync, + private val streamHandlerExecutor: Executor, private val params: VectorStoreListParams, private val response: VectorStoreListPageResponse, -) { +) : PageAsync { /** * Delegates to [VectorStoreListPageResponse], but gracefully handles missing data. @@ -33,22 +35,17 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): VectorStoreListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = + service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): VectorStoreListParams = params @@ -66,6 +63,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -77,18 +75,24 @@ private constructor( class Builder internal constructor() { private var service: VectorStoreServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: VectorStoreListParams? = null private var response: VectorStoreListPageResponse? = null @JvmSynthetic internal fun from(vectorStoreListPageAsync: VectorStoreListPageAsync) = apply { service = vectorStoreListPageAsync.service + streamHandlerExecutor = vectorStoreListPageAsync.streamHandlerExecutor params = vectorStoreListPageAsync.params response = vectorStoreListPageAsync.response } fun service(service: VectorStoreServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: VectorStoreListParams) = apply { this.params = params } @@ -103,6 +107,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -112,47 +117,22 @@ private constructor( fun build(): VectorStoreListPageAsync = VectorStoreListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: VectorStoreListPageAsync) { - - fun forEach(action: Predicate, executor: Executor): CompletableFuture { - fun CompletableFuture>.forEach( - action: (VectorStore) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is VectorStoreListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is VectorStoreListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "VectorStoreListPageAsync{service=$service, params=$params, response=$response}" + "VectorStoreListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreRetrieveParams.kt index f8fc087d..87d3f482 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreRetrieveParams.kt @@ -3,20 +3,21 @@ package com.openai.models.vectorstores import com.openai.core.Params -import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Retrieves a vector store. */ class VectorStoreRetrieveParams private constructor( - private val vectorStoreId: String, + private val vectorStoreId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun vectorStoreId(): String = vectorStoreId + fun vectorStoreId(): Optional = Optional.ofNullable(vectorStoreId) fun _additionalHeaders(): Headers = additionalHeaders @@ -26,13 +27,10 @@ private constructor( companion object { + @JvmStatic fun none(): VectorStoreRetrieveParams = builder().build() + /** * Returns a mutable builder for constructing an instance of [VectorStoreRetrieveParams]. - * - * The following fields are required: - * ```java - * .vectorStoreId() - * ``` */ @JvmStatic fun builder() = Builder() } @@ -51,7 +49,11 @@ private constructor( additionalQueryParams = vectorStoreRetrieveParams.additionalQueryParams.toBuilder() } - fun vectorStoreId(vectorStoreId: String) = apply { this.vectorStoreId = vectorStoreId } + fun vectorStoreId(vectorStoreId: String?) = apply { this.vectorStoreId = vectorStoreId } + + /** Alias for calling [Builder.vectorStoreId] with `vectorStoreId.orElse(null)`. */ + fun vectorStoreId(vectorStoreId: Optional) = + vectorStoreId(vectorStoreId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -155,17 +157,10 @@ private constructor( * Returns an immutable instance of [VectorStoreRetrieveParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .vectorStoreId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): VectorStoreRetrieveParams = VectorStoreRetrieveParams( - checkRequired("vectorStoreId", vectorStoreId), + vectorStoreId, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -173,7 +168,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> vectorStoreId + 0 -> vectorStoreId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPage.kt index fcdefb6c..a45345a0 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPage.kt @@ -2,13 +2,12 @@ package com.openai.models.vectorstores +import com.openai.core.AutoPager import com.openai.core.JsonValue +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.VectorStoreService import java.util.Objects -import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [VectorStoreService.search] */ @@ -17,7 +16,7 @@ private constructor( private val service: VectorStoreService, private val params: VectorStoreSearchParams, private val response: VectorStoreSearchPageResponse, -) { +) : Page { /** * Delegates to [VectorStoreSearchPageResponse], but gracefully handles missing data. @@ -30,14 +29,16 @@ private constructor( /** @see [VectorStoreSearchPageResponse.object_] */ fun object_(): JsonValue = response._object_() - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional = Optional.empty() + override fun hasNextPage(): Boolean = items().isNotEmpty() - fun getNextPage(): Optional = - getNextPageParams().map { service.search(it) } + fun nextPageParams(): VectorStoreSearchParams = + throw IllegalStateException("Cannot construct next page params") - fun autoPager(): AutoPager = AutoPager(this) + override fun nextPage(): VectorStoreSearchPage = service.search(nextPageParams()) + + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): VectorStoreSearchParams = params @@ -106,26 +107,6 @@ private constructor( ) } - class AutoPager(private val firstPage: VectorStoreSearchPage) : - Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPageAsync.kt index 0b7c1d85..00431ef9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPageAsync.kt @@ -2,23 +2,24 @@ package com.openai.models.vectorstores +import com.openai.core.AutoPagerAsync import com.openai.core.JsonValue +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.VectorStoreServiceAsync import java.util.Objects -import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [VectorStoreServiceAsync.search] */ class VectorStoreSearchPageAsync private constructor( private val service: VectorStoreServiceAsync, + private val streamHandlerExecutor: Executor, private val params: VectorStoreSearchParams, private val response: VectorStoreSearchPageResponse, -) { +) : PageAsync { /** * Delegates to [VectorStoreSearchPageResponse], but gracefully handles missing data. @@ -31,16 +32,18 @@ private constructor( /** @see [VectorStoreSearchPageResponse.object_] */ fun object_(): JsonValue = response._object_() - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional = Optional.empty() + override fun hasNextPage(): Boolean = items().isNotEmpty() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.search(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + fun nextPageParams(): VectorStoreSearchParams = + throw IllegalStateException("Cannot construct next page params") - fun autoPager(): AutoPager = AutoPager(this) + override fun nextPage(): CompletableFuture = + service.search(nextPageParams()) + + fun autoPager(): AutoPagerAsync = + AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): VectorStoreSearchParams = params @@ -58,6 +61,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -69,18 +73,24 @@ private constructor( class Builder internal constructor() { private var service: VectorStoreServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: VectorStoreSearchParams? = null private var response: VectorStoreSearchPageResponse? = null @JvmSynthetic internal fun from(vectorStoreSearchPageAsync: VectorStoreSearchPageAsync) = apply { service = vectorStoreSearchPageAsync.service + streamHandlerExecutor = vectorStoreSearchPageAsync.streamHandlerExecutor params = vectorStoreSearchPageAsync.params response = vectorStoreSearchPageAsync.response } fun service(service: VectorStoreServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: VectorStoreSearchParams) = apply { this.params = params } @@ -95,6 +105,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -104,50 +115,22 @@ private constructor( fun build(): VectorStoreSearchPageAsync = VectorStoreSearchPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: VectorStoreSearchPageAsync) { - - fun forEach( - action: Predicate, - executor: Executor, - ): CompletableFuture { - fun CompletableFuture>.forEach( - action: (VectorStoreSearchResponse) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is VectorStoreSearchPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is VectorStoreSearchPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "VectorStoreSearchPageAsync{service=$service, params=$params, response=$response}" + "VectorStoreSearchPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchParams.kt index 849e5ab3..0add3f17 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchParams.kt @@ -37,13 +37,13 @@ import kotlin.jvm.optionals.getOrNull /** Search a vector store for relevant chunks based on a query and file attributes filter. */ class VectorStoreSearchParams private constructor( - private val vectorStoreId: String, + private val vectorStoreId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun vectorStoreId(): String = vectorStoreId + fun vectorStoreId(): Optional = Optional.ofNullable(vectorStoreId) /** * A query string for a search @@ -135,7 +135,6 @@ private constructor( * * The following fields are required: * ```java - * .vectorStoreId() * .query() * ``` */ @@ -158,7 +157,11 @@ private constructor( additionalQueryParams = vectorStoreSearchParams.additionalQueryParams.toBuilder() } - fun vectorStoreId(vectorStoreId: String) = apply { this.vectorStoreId = vectorStoreId } + fun vectorStoreId(vectorStoreId: String?) = apply { this.vectorStoreId = vectorStoreId } + + /** Alias for calling [Builder.vectorStoreId] with `vectorStoreId.orElse(null)`. */ + fun vectorStoreId(vectorStoreId: Optional) = + vectorStoreId(vectorStoreId.getOrNull()) /** * Sets the entire request body. @@ -379,7 +382,6 @@ private constructor( * * The following fields are required: * ```java - * .vectorStoreId() * .query() * ``` * @@ -387,7 +389,7 @@ private constructor( */ fun build(): VectorStoreSearchParams = VectorStoreSearchParams( - checkRequired("vectorStoreId", vectorStoreId), + vectorStoreId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -398,7 +400,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> vectorStoreId + 0 -> vectorStoreId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreUpdateParams.kt index e28e249a..905256cb 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreUpdateParams.kt @@ -24,13 +24,13 @@ import kotlin.jvm.optionals.getOrNull /** Modifies a vector store. */ class VectorStoreUpdateParams private constructor( - private val vectorStoreId: String, + private val vectorStoreId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun vectorStoreId(): String = vectorStoreId + fun vectorStoreId(): Optional = Optional.ofNullable(vectorStoreId) /** * The expiration policy for a vector store. @@ -92,14 +92,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [VectorStoreUpdateParams]. - * - * The following fields are required: - * ```java - * .vectorStoreId() - * ``` - */ + @JvmStatic fun none(): VectorStoreUpdateParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [VectorStoreUpdateParams]. */ @JvmStatic fun builder() = Builder() } @@ -119,7 +114,11 @@ private constructor( additionalQueryParams = vectorStoreUpdateParams.additionalQueryParams.toBuilder() } - fun vectorStoreId(vectorStoreId: String) = apply { this.vectorStoreId = vectorStoreId } + fun vectorStoreId(vectorStoreId: String?) = apply { this.vectorStoreId = vectorStoreId } + + /** Alias for calling [Builder.vectorStoreId] with `vectorStoreId.orElse(null)`. */ + fun vectorStoreId(vectorStoreId: Optional) = + vectorStoreId(vectorStoreId.getOrNull()) /** * Sets the entire request body. @@ -307,17 +306,10 @@ private constructor( * Returns an immutable instance of [VectorStoreUpdateParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .vectorStoreId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): VectorStoreUpdateParams = VectorStoreUpdateParams( - checkRequired("vectorStoreId", vectorStoreId), + vectorStoreId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -328,7 +320,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> vectorStoreId + 0 -> vectorStoreId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchCancelParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchCancelParams.kt index 1cc88512..e317488b 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchCancelParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchCancelParams.kt @@ -10,6 +10,7 @@ import com.openai.core.http.QueryParams import com.openai.core.toImmutable import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Cancel a vector store file batch. This attempts to cancel the processing of files in this batch @@ -18,7 +19,7 @@ import java.util.Optional class FileBatchCancelParams private constructor( private val vectorStoreId: String, - private val batchId: String, + private val batchId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, @@ -26,7 +27,7 @@ private constructor( fun vectorStoreId(): String = vectorStoreId - fun batchId(): String = batchId + fun batchId(): Optional = Optional.ofNullable(batchId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -44,7 +45,6 @@ private constructor( * The following fields are required: * ```java * .vectorStoreId() - * .batchId() * ``` */ @JvmStatic fun builder() = Builder() @@ -70,7 +70,10 @@ private constructor( fun vectorStoreId(vectorStoreId: String) = apply { this.vectorStoreId = vectorStoreId } - fun batchId(batchId: String) = apply { this.batchId = batchId } + fun batchId(batchId: String?) = apply { this.batchId = batchId } + + /** Alias for calling [Builder.batchId] with `batchId.orElse(null)`. */ + fun batchId(batchId: Optional) = batchId(batchId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -200,7 +203,6 @@ private constructor( * The following fields are required: * ```java * .vectorStoreId() - * .batchId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -208,7 +210,7 @@ private constructor( fun build(): FileBatchCancelParams = FileBatchCancelParams( checkRequired("vectorStoreId", vectorStoreId), - checkRequired("batchId", batchId), + batchId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -221,7 +223,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> vectorStoreId - 1 -> batchId + 1 -> batchId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchCreateParams.kt index 6109a4f1..e515321c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchCreateParams.kt @@ -29,13 +29,13 @@ import kotlin.jvm.optionals.getOrNull /** Create a vector store file batch. */ class FileBatchCreateParams private constructor( - private val vectorStoreId: String, + private val vectorStoreId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun vectorStoreId(): String = vectorStoreId + fun vectorStoreId(): Optional = Optional.ofNullable(vectorStoreId) /** * A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that the vector @@ -103,7 +103,6 @@ private constructor( * * The following fields are required: * ```java - * .vectorStoreId() * .fileIds() * ``` */ @@ -126,7 +125,11 @@ private constructor( additionalQueryParams = fileBatchCreateParams.additionalQueryParams.toBuilder() } - fun vectorStoreId(vectorStoreId: String) = apply { this.vectorStoreId = vectorStoreId } + fun vectorStoreId(vectorStoreId: String?) = apply { this.vectorStoreId = vectorStoreId } + + /** Alias for calling [Builder.vectorStoreId] with `vectorStoreId.orElse(null)`. */ + fun vectorStoreId(vectorStoreId: Optional) = + vectorStoreId(vectorStoreId.getOrNull()) /** * Sets the entire request body. @@ -349,7 +352,6 @@ private constructor( * * The following fields are required: * ```java - * .vectorStoreId() * .fileIds() * ``` * @@ -357,7 +359,7 @@ private constructor( */ fun build(): FileBatchCreateParams = FileBatchCreateParams( - checkRequired("vectorStoreId", vectorStoreId), + vectorStoreId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -368,7 +370,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> vectorStoreId + 0 -> vectorStoreId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPage.kt index 42d724d5..96a6d8b3 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPage.kt @@ -2,13 +2,13 @@ package com.openai.models.vectorstores.filebatches +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.models.vectorstores.files.VectorStoreFile import com.openai.services.blocking.vectorstores.FileBatchService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [FileBatchService.listFiles] */ @@ -17,7 +17,7 @@ private constructor( private val service: FileBatchService, private val params: FileBatchListFilesParams, private val response: FileBatchListFilesPageResponse, -) { +) : Page { /** * Delegates to [FileBatchListFilesPageResponse], but gracefully handles missing data. @@ -34,20 +34,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): FileBatchListFilesParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = - getNextPageParams().map { service.listFiles(it) } + override fun nextPage(): FileBatchListFilesPage = service.listFiles(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): FileBatchListFilesParams = params @@ -116,25 +112,6 @@ private constructor( ) } - class AutoPager(private val firstPage: FileBatchListFilesPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPageAsync.kt index 7c52c45b..c6ea04c1 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPageAsync.kt @@ -2,6 +2,8 @@ package com.openai.models.vectorstores.filebatches +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.models.vectorstores.files.VectorStoreFile import com.openai.services.async.vectorstores.FileBatchServiceAsync @@ -9,16 +11,16 @@ import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [FileBatchServiceAsync.listFiles] */ class FileBatchListFilesPageAsync private constructor( private val service: FileBatchServiceAsync, + private val streamHandlerExecutor: Executor, private val params: FileBatchListFilesParams, private val response: FileBatchListFilesPageResponse, -) { +) : PageAsync { /** * Delegates to [FileBatchListFilesPageResponse], but gracefully handles missing data. @@ -35,22 +37,18 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): FileBatchListFilesParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.listFiles(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = + service.listFiles(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = + AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): FileBatchListFilesParams = params @@ -68,6 +66,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -79,18 +78,24 @@ private constructor( class Builder internal constructor() { private var service: FileBatchServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: FileBatchListFilesParams? = null private var response: FileBatchListFilesPageResponse? = null @JvmSynthetic internal fun from(fileBatchListFilesPageAsync: FileBatchListFilesPageAsync) = apply { service = fileBatchListFilesPageAsync.service + streamHandlerExecutor = fileBatchListFilesPageAsync.streamHandlerExecutor params = fileBatchListFilesPageAsync.params response = fileBatchListFilesPageAsync.response } fun service(service: FileBatchServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: FileBatchListFilesParams) = apply { this.params = params } @@ -105,6 +110,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -114,50 +120,22 @@ private constructor( fun build(): FileBatchListFilesPageAsync = FileBatchListFilesPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: FileBatchListFilesPageAsync) { - - fun forEach( - action: Predicate, - executor: Executor, - ): CompletableFuture { - fun CompletableFuture>.forEach( - action: (VectorStoreFile) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is FileBatchListFilesPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is FileBatchListFilesPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "FileBatchListFilesPageAsync{service=$service, params=$params, response=$response}" + "FileBatchListFilesPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesParams.kt index 2be57ce8..e0b95a1d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesParams.kt @@ -18,7 +18,7 @@ import kotlin.jvm.optionals.getOrNull class FileBatchListFilesParams private constructor( private val vectorStoreId: String, - private val batchId: String, + private val batchId: String?, private val after: String?, private val before: String?, private val filter: Filter?, @@ -30,7 +30,7 @@ private constructor( fun vectorStoreId(): String = vectorStoreId - fun batchId(): String = batchId + fun batchId(): Optional = Optional.ofNullable(batchId) /** * A cursor for use in pagination. `after` is an object ID that defines your place in the list. @@ -75,7 +75,6 @@ private constructor( * The following fields are required: * ```java * .vectorStoreId() - * .batchId() * ``` */ @JvmStatic fun builder() = Builder() @@ -109,7 +108,10 @@ private constructor( fun vectorStoreId(vectorStoreId: String) = apply { this.vectorStoreId = vectorStoreId } - fun batchId(batchId: String) = apply { this.batchId = batchId } + fun batchId(batchId: String?) = apply { this.batchId = batchId } + + /** Alias for calling [Builder.batchId] with `batchId.orElse(null)`. */ + fun batchId(batchId: Optional) = batchId(batchId.getOrNull()) /** * A cursor for use in pagination. `after` is an object ID that defines your place in the @@ -270,7 +272,6 @@ private constructor( * The following fields are required: * ```java * .vectorStoreId() - * .batchId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -278,7 +279,7 @@ private constructor( fun build(): FileBatchListFilesParams = FileBatchListFilesParams( checkRequired("vectorStoreId", vectorStoreId), - checkRequired("batchId", batchId), + batchId, after, before, filter, @@ -292,7 +293,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> vectorStoreId - 1 -> batchId + 1 -> batchId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchRetrieveParams.kt index de2b16bd..8a3acc77 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchRetrieveParams.kt @@ -7,19 +7,21 @@ import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Retrieves a vector store file batch. */ class FileBatchRetrieveParams private constructor( private val vectorStoreId: String, - private val batchId: String, + private val batchId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun vectorStoreId(): String = vectorStoreId - fun batchId(): String = batchId + fun batchId(): Optional = Optional.ofNullable(batchId) fun _additionalHeaders(): Headers = additionalHeaders @@ -35,7 +37,6 @@ private constructor( * The following fields are required: * ```java * .vectorStoreId() - * .batchId() * ``` */ @JvmStatic fun builder() = Builder() @@ -59,7 +60,10 @@ private constructor( fun vectorStoreId(vectorStoreId: String) = apply { this.vectorStoreId = vectorStoreId } - fun batchId(batchId: String) = apply { this.batchId = batchId } + fun batchId(batchId: String?) = apply { this.batchId = batchId } + + /** Alias for calling [Builder.batchId] with `batchId.orElse(null)`. */ + fun batchId(batchId: Optional) = batchId(batchId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -167,7 +171,6 @@ private constructor( * The following fields are required: * ```java * .vectorStoreId() - * .batchId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -175,7 +178,7 @@ private constructor( fun build(): FileBatchRetrieveParams = FileBatchRetrieveParams( checkRequired("vectorStoreId", vectorStoreId), - checkRequired("batchId", batchId), + batchId, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -184,7 +187,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> vectorStoreId - 1 -> batchId + 1 -> batchId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPage.kt index 6bbd3084..e1980ef9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPage.kt @@ -2,13 +2,12 @@ package com.openai.models.vectorstores.files +import com.openai.core.AutoPager import com.openai.core.JsonValue +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.vectorstores.FileService import java.util.Objects -import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [FileService.content] */ @@ -17,7 +16,7 @@ private constructor( private val service: FileService, private val params: FileContentParams, private val response: FileContentPageResponse, -) { +) : Page { /** * Delegates to [FileContentPageResponse], but gracefully handles missing data. @@ -30,13 +29,16 @@ private constructor( /** @see [FileContentPageResponse.object_] */ fun object_(): JsonValue = response._object_() - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional = Optional.empty() + override fun hasNextPage(): Boolean = items().isNotEmpty() - fun getNextPage(): Optional = getNextPageParams().map { service.content(it) } + fun nextPageParams(): FileContentParams = + throw IllegalStateException("Cannot construct next page params") - fun autoPager(): AutoPager = AutoPager(this) + override fun nextPage(): FileContentPage = service.content(nextPageParams()) + + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): FileContentParams = params @@ -105,25 +107,6 @@ private constructor( ) } - class AutoPager(private val firstPage: FileContentPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPageAsync.kt index a55657f1..30fb9db2 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPageAsync.kt @@ -2,23 +2,24 @@ package com.openai.models.vectorstores.files +import com.openai.core.AutoPagerAsync import com.openai.core.JsonValue +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.vectorstores.FileServiceAsync import java.util.Objects -import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [FileServiceAsync.content] */ class FileContentPageAsync private constructor( private val service: FileServiceAsync, + private val streamHandlerExecutor: Executor, private val params: FileContentParams, private val response: FileContentPageResponse, -) { +) : PageAsync { /** * Delegates to [FileContentPageResponse], but gracefully handles missing data. @@ -31,16 +32,18 @@ private constructor( /** @see [FileContentPageResponse.object_] */ fun object_(): JsonValue = response._object_() - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional = Optional.empty() + override fun hasNextPage(): Boolean = items().isNotEmpty() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.content(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + fun nextPageParams(): FileContentParams = + throw IllegalStateException("Cannot construct next page params") - fun autoPager(): AutoPager = AutoPager(this) + override fun nextPage(): CompletableFuture = + service.content(nextPageParams()) + + fun autoPager(): AutoPagerAsync = + AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): FileContentParams = params @@ -58,6 +61,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -69,18 +73,24 @@ private constructor( class Builder internal constructor() { private var service: FileServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: FileContentParams? = null private var response: FileContentPageResponse? = null @JvmSynthetic internal fun from(fileContentPageAsync: FileContentPageAsync) = apply { service = fileContentPageAsync.service + streamHandlerExecutor = fileContentPageAsync.streamHandlerExecutor params = fileContentPageAsync.params response = fileContentPageAsync.response } fun service(service: FileServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: FileContentParams) = apply { this.params = params } @@ -95,6 +105,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -104,50 +115,22 @@ private constructor( fun build(): FileContentPageAsync = FileContentPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: FileContentPageAsync) { - - fun forEach( - action: Predicate, - executor: Executor, - ): CompletableFuture { - fun CompletableFuture>.forEach( - action: (FileContentResponse) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is FileContentPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is FileContentPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "FileContentPageAsync{service=$service, params=$params, response=$response}" + "FileContentPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentParams.kt index e52fe40d..24503213 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentParams.kt @@ -7,19 +7,21 @@ import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Retrieve the parsed contents of a vector store file. */ class FileContentParams private constructor( private val vectorStoreId: String, - private val fileId: String, + private val fileId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun vectorStoreId(): String = vectorStoreId - fun fileId(): String = fileId + fun fileId(): Optional = Optional.ofNullable(fileId) fun _additionalHeaders(): Headers = additionalHeaders @@ -35,7 +37,6 @@ private constructor( * The following fields are required: * ```java * .vectorStoreId() - * .fileId() * ``` */ @JvmStatic fun builder() = Builder() @@ -59,7 +60,10 @@ private constructor( fun vectorStoreId(vectorStoreId: String) = apply { this.vectorStoreId = vectorStoreId } - fun fileId(fileId: String) = apply { this.fileId = fileId } + fun fileId(fileId: String?) = apply { this.fileId = fileId } + + /** Alias for calling [Builder.fileId] with `fileId.orElse(null)`. */ + fun fileId(fileId: Optional) = fileId(fileId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -167,7 +171,6 @@ private constructor( * The following fields are required: * ```java * .vectorStoreId() - * .fileId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -175,7 +178,7 @@ private constructor( fun build(): FileContentParams = FileContentParams( checkRequired("vectorStoreId", vectorStoreId), - checkRequired("fileId", fileId), + fileId, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -184,7 +187,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> vectorStoreId - 1 -> fileId + 1 -> fileId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileCreateParams.kt index 339559a0..d6c0af37 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileCreateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileCreateParams.kt @@ -32,13 +32,13 @@ import kotlin.jvm.optionals.getOrNull */ class FileCreateParams private constructor( - private val vectorStoreId: String, + private val vectorStoreId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun vectorStoreId(): String = vectorStoreId + fun vectorStoreId(): Optional = Optional.ofNullable(vectorStoreId) /** * A [File](https://platform.openai.com/docs/api-reference/files) ID that the vector store @@ -106,7 +106,6 @@ private constructor( * * The following fields are required: * ```java - * .vectorStoreId() * .fileId() * ``` */ @@ -129,7 +128,11 @@ private constructor( additionalQueryParams = fileCreateParams.additionalQueryParams.toBuilder() } - fun vectorStoreId(vectorStoreId: String) = apply { this.vectorStoreId = vectorStoreId } + fun vectorStoreId(vectorStoreId: String?) = apply { this.vectorStoreId = vectorStoreId } + + /** Alias for calling [Builder.vectorStoreId] with `vectorStoreId.orElse(null)`. */ + fun vectorStoreId(vectorStoreId: Optional) = + vectorStoreId(vectorStoreId.getOrNull()) /** * Sets the entire request body. @@ -344,7 +347,6 @@ private constructor( * * The following fields are required: * ```java - * .vectorStoreId() * .fileId() * ``` * @@ -352,7 +354,7 @@ private constructor( */ fun build(): FileCreateParams = FileCreateParams( - checkRequired("vectorStoreId", vectorStoreId), + vectorStoreId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -363,7 +365,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> vectorStoreId + 0 -> vectorStoreId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileDeleteParams.kt index 7ac510f2..c739cb62 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileDeleteParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileDeleteParams.kt @@ -10,6 +10,7 @@ import com.openai.core.http.QueryParams import com.openai.core.toImmutable import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * Delete a vector store file. This will remove the file from the vector store but the file itself @@ -19,7 +20,7 @@ import java.util.Optional class FileDeleteParams private constructor( private val vectorStoreId: String, - private val fileId: String, + private val fileId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, @@ -27,7 +28,7 @@ private constructor( fun vectorStoreId(): String = vectorStoreId - fun fileId(): String = fileId + fun fileId(): Optional = Optional.ofNullable(fileId) fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -45,7 +46,6 @@ private constructor( * The following fields are required: * ```java * .vectorStoreId() - * .fileId() * ``` */ @JvmStatic fun builder() = Builder() @@ -71,7 +71,10 @@ private constructor( fun vectorStoreId(vectorStoreId: String) = apply { this.vectorStoreId = vectorStoreId } - fun fileId(fileId: String) = apply { this.fileId = fileId } + fun fileId(fileId: String?) = apply { this.fileId = fileId } + + /** Alias for calling [Builder.fileId] with `fileId.orElse(null)`. */ + fun fileId(fileId: Optional) = fileId(fileId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -201,7 +204,6 @@ private constructor( * The following fields are required: * ```java * .vectorStoreId() - * .fileId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -209,7 +211,7 @@ private constructor( fun build(): FileDeleteParams = FileDeleteParams( checkRequired("vectorStoreId", vectorStoreId), - checkRequired("fileId", fileId), + fileId, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -222,7 +224,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> vectorStoreId - 1 -> fileId + 1 -> fileId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPage.kt index 51c03261..d77b54c0 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPage.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPage.kt @@ -2,12 +2,12 @@ package com.openai.models.vectorstores.files +import com.openai.core.AutoPager +import com.openai.core.Page import com.openai.core.checkRequired import com.openai.services.blocking.vectorstores.FileService import java.util.Objects import java.util.Optional -import java.util.stream.Stream -import java.util.stream.StreamSupport import kotlin.jvm.optionals.getOrNull /** @see [FileService.list] */ @@ -16,7 +16,7 @@ private constructor( private val service: FileService, private val params: FileListParams, private val response: FileListPageResponse, -) { +) : Page { /** * Delegates to [FileListPageResponse], but gracefully handles missing data. @@ -33,19 +33,16 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): FileListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): Optional = getNextPageParams().map { service.list(it) } + override fun nextPage(): FileListPage = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPager = AutoPager.from(this) /** The parameters that were used to request this page. */ fun params(): FileListParams = params @@ -114,25 +111,6 @@ private constructor( ) } - class AutoPager(private val firstPage: FileListPage) : Iterable { - - override fun iterator(): Iterator = iterator { - var page = firstPage - var index = 0 - while (true) { - while (index < page.data().size) { - yield(page.data()[index++]) - } - page = page.getNextPage().getOrNull() ?: break - index = 0 - } - } - - fun stream(): Stream { - return StreamSupport.stream(spliterator(), false) - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPageAsync.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPageAsync.kt index ace12328..f4211867 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPageAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPageAsync.kt @@ -2,22 +2,24 @@ package com.openai.models.vectorstores.files +import com.openai.core.AutoPagerAsync +import com.openai.core.PageAsync import com.openai.core.checkRequired import com.openai.services.async.vectorstores.FileServiceAsync import java.util.Objects import java.util.Optional import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor -import java.util.function.Predicate import kotlin.jvm.optionals.getOrNull /** @see [FileServiceAsync.list] */ class FileListPageAsync private constructor( private val service: FileServiceAsync, + private val streamHandlerExecutor: Executor, private val params: FileListParams, private val response: FileListPageResponse, -) { +) : PageAsync { /** * Delegates to [FileListPageResponse], but gracefully handles missing data. @@ -34,22 +36,17 @@ private constructor( */ fun hasMore(): Optional = response._hasMore().getOptional("has_more") - fun hasNextPage(): Boolean = data().isNotEmpty() + override fun items(): List = data() - fun getNextPageParams(): Optional { - if (!hasNextPage()) { - return Optional.empty() - } + override fun hasNextPage(): Boolean = items().isNotEmpty() - return Optional.of(params.toBuilder().after(data().last()._id().getOptional("id")).build()) - } + fun nextPageParams(): FileListParams = + params.toBuilder().after(items().last()._id().getOptional("id")).build() - fun getNextPage(): CompletableFuture> = - getNextPageParams() - .map { service.list(it).thenApply { Optional.of(it) } } - .orElseGet { CompletableFuture.completedFuture(Optional.empty()) } + override fun nextPage(): CompletableFuture = service.list(nextPageParams()) - fun autoPager(): AutoPager = AutoPager(this) + fun autoPager(): AutoPagerAsync = + AutoPagerAsync.from(this, streamHandlerExecutor) /** The parameters that were used to request this page. */ fun params(): FileListParams = params @@ -67,6 +64,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -78,18 +76,24 @@ private constructor( class Builder internal constructor() { private var service: FileServiceAsync? = null + private var streamHandlerExecutor: Executor? = null private var params: FileListParams? = null private var response: FileListPageResponse? = null @JvmSynthetic internal fun from(fileListPageAsync: FileListPageAsync) = apply { service = fileListPageAsync.service + streamHandlerExecutor = fileListPageAsync.streamHandlerExecutor params = fileListPageAsync.params response = fileListPageAsync.response } fun service(service: FileServiceAsync) = apply { this.service = service } + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = streamHandlerExecutor + } + /** The parameters that were used to request this page. */ fun params(params: FileListParams) = apply { this.params = params } @@ -104,6 +108,7 @@ private constructor( * The following fields are required: * ```java * .service() + * .streamHandlerExecutor() * .params() * .response() * ``` @@ -113,50 +118,22 @@ private constructor( fun build(): FileListPageAsync = FileListPageAsync( checkRequired("service", service), + checkRequired("streamHandlerExecutor", streamHandlerExecutor), checkRequired("params", params), checkRequired("response", response), ) } - class AutoPager(private val firstPage: FileListPageAsync) { - - fun forEach( - action: Predicate, - executor: Executor, - ): CompletableFuture { - fun CompletableFuture>.forEach( - action: (VectorStoreFile) -> Boolean, - executor: Executor, - ): CompletableFuture = - thenComposeAsync( - { page -> - page - .filter { it.data().all(action) } - .map { it.getNextPage().forEach(action, executor) } - .orElseGet { CompletableFuture.completedFuture(null) } - }, - executor, - ) - return CompletableFuture.completedFuture(Optional.of(firstPage)) - .forEach(action::test, executor) - } - - fun toList(executor: Executor): CompletableFuture> { - val values = mutableListOf() - return forEach(values::add, executor).thenApply { values } - } - } - override fun equals(other: Any?): Boolean { if (this === other) { return true } - return /* spotless:off */ other is FileListPageAsync && service == other.service && params == other.params && response == other.response /* spotless:on */ + return /* spotless:off */ other is FileListPageAsync && service == other.service && streamHandlerExecutor == other.streamHandlerExecutor && params == other.params && response == other.response /* spotless:on */ } - override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, params, response) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(service, streamHandlerExecutor, params, response) /* spotless:on */ override fun toString() = - "FileListPageAsync{service=$service, params=$params, response=$response}" + "FileListPageAsync{service=$service, streamHandlerExecutor=$streamHandlerExecutor, params=$params, response=$response}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListParams.kt index 4f0240de..c2ca930c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListParams.kt @@ -6,7 +6,6 @@ import com.fasterxml.jackson.annotation.JsonCreator import com.openai.core.Enum import com.openai.core.JsonField 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.errors.OpenAIInvalidDataException @@ -17,7 +16,7 @@ import kotlin.jvm.optionals.getOrNull /** Returns a list of vector store files. */ class FileListParams private constructor( - private val vectorStoreId: String, + private val vectorStoreId: String?, private val after: String?, private val before: String?, private val filter: Filter?, @@ -27,7 +26,7 @@ private constructor( private val additionalQueryParams: QueryParams, ) : Params { - fun vectorStoreId(): String = vectorStoreId + fun vectorStoreId(): Optional = Optional.ofNullable(vectorStoreId) /** * A cursor for use in pagination. `after` is an object ID that defines your place in the list. @@ -66,14 +65,9 @@ private constructor( companion object { - /** - * Returns a mutable builder for constructing an instance of [FileListParams]. - * - * The following fields are required: - * ```java - * .vectorStoreId() - * ``` - */ + @JvmStatic fun none(): FileListParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [FileListParams]. */ @JvmStatic fun builder() = Builder() } @@ -101,7 +95,11 @@ private constructor( additionalQueryParams = fileListParams.additionalQueryParams.toBuilder() } - fun vectorStoreId(vectorStoreId: String) = apply { this.vectorStoreId = vectorStoreId } + fun vectorStoreId(vectorStoreId: String?) = apply { this.vectorStoreId = vectorStoreId } + + /** Alias for calling [Builder.vectorStoreId] with `vectorStoreId.orElse(null)`. */ + fun vectorStoreId(vectorStoreId: Optional) = + vectorStoreId(vectorStoreId.getOrNull()) /** * A cursor for use in pagination. `after` is an object ID that defines your place in the @@ -258,17 +256,10 @@ private constructor( * Returns an immutable instance of [FileListParams]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```java - * .vectorStoreId() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ fun build(): FileListParams = FileListParams( - checkRequired("vectorStoreId", vectorStoreId), + vectorStoreId, after, before, filter, @@ -281,7 +272,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> vectorStoreId + 0 -> vectorStoreId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileRetrieveParams.kt index 5ab12455..f81b1127 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileRetrieveParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileRetrieveParams.kt @@ -7,19 +7,21 @@ import com.openai.core.checkRequired import com.openai.core.http.Headers import com.openai.core.http.QueryParams import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** Retrieves a vector store file. */ class FileRetrieveParams private constructor( private val vectorStoreId: String, - private val fileId: String, + private val fileId: String?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { fun vectorStoreId(): String = vectorStoreId - fun fileId(): String = fileId + fun fileId(): Optional = Optional.ofNullable(fileId) fun _additionalHeaders(): Headers = additionalHeaders @@ -35,7 +37,6 @@ private constructor( * The following fields are required: * ```java * .vectorStoreId() - * .fileId() * ``` */ @JvmStatic fun builder() = Builder() @@ -59,7 +60,10 @@ private constructor( fun vectorStoreId(vectorStoreId: String) = apply { this.vectorStoreId = vectorStoreId } - fun fileId(fileId: String) = apply { this.fileId = fileId } + fun fileId(fileId: String?) = apply { this.fileId = fileId } + + /** Alias for calling [Builder.fileId] with `fileId.orElse(null)`. */ + fun fileId(fileId: Optional) = fileId(fileId.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -167,7 +171,6 @@ private constructor( * The following fields are required: * ```java * .vectorStoreId() - * .fileId() * ``` * * @throws IllegalStateException if any required field is unset. @@ -175,7 +178,7 @@ private constructor( fun build(): FileRetrieveParams = FileRetrieveParams( checkRequired("vectorStoreId", vectorStoreId), - checkRequired("fileId", fileId), + fileId, additionalHeaders.build(), additionalQueryParams.build(), ) @@ -184,7 +187,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> vectorStoreId - 1 -> fileId + 1 -> fileId ?: "" else -> "" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileUpdateParams.kt index c5dbccbd..2377d4d2 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileUpdateParams.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileUpdateParams.kt @@ -25,7 +25,7 @@ import kotlin.jvm.optionals.getOrNull class FileUpdateParams private constructor( private val vectorStoreId: String, - private val fileId: String, + private val fileId: String?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, @@ -33,7 +33,7 @@ private constructor( fun vectorStoreId(): String = vectorStoreId - fun fileId(): String = fileId + fun fileId(): Optional = Optional.ofNullable(fileId) /** * Set of 16 key-value pairs that can be attached to an object. This can be useful for storing @@ -69,7 +69,6 @@ private constructor( * The following fields are required: * ```java * .vectorStoreId() - * .fileId() * .attributes() * ``` */ @@ -96,7 +95,10 @@ private constructor( fun vectorStoreId(vectorStoreId: String) = apply { this.vectorStoreId = vectorStoreId } - fun fileId(fileId: String) = apply { this.fileId = fileId } + fun fileId(fileId: String?) = apply { this.fileId = fileId } + + /** Alias for calling [Builder.fileId] with `fileId.orElse(null)`. */ + fun fileId(fileId: Optional) = fileId(fileId.getOrNull()) /** * Sets the entire request body. @@ -253,7 +255,6 @@ private constructor( * The following fields are required: * ```java * .vectorStoreId() - * .fileId() * .attributes() * ``` * @@ -262,7 +263,7 @@ private constructor( fun build(): FileUpdateParams = FileUpdateParams( checkRequired("vectorStoreId", vectorStoreId), - checkRequired("fileId", fileId), + fileId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -274,7 +275,7 @@ private constructor( fun _pathParam(index: Int): String = when (index) { 0 -> vectorStoreId - 1 -> fileId + 1 -> fileId ?: "" else -> "" } 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 45dd0c24..84bc4043 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 @@ -31,8 +31,22 @@ interface BatchServiceAsync { ): CompletableFuture /** Retrieves a batch. */ - fun retrieve(params: BatchRetrieveParams): CompletableFuture = - retrieve(params, RequestOptions.none()) + fun retrieve(batchId: String): CompletableFuture = + retrieve(batchId, BatchRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + batchId: String, + params: BatchRetrieveParams = BatchRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + batchId: String, + params: BatchRetrieveParams = BatchRetrieveParams.none(), + ): CompletableFuture = retrieve(batchId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -40,6 +54,14 @@ interface BatchServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [retrieve] */ + fun retrieve(params: BatchRetrieveParams): CompletableFuture = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve(batchId: String, requestOptions: RequestOptions): CompletableFuture = + retrieve(batchId, BatchRetrieveParams.none(), requestOptions) + /** List your organization's batches. */ fun list(): CompletableFuture = list(BatchListParams.none()) @@ -63,8 +85,22 @@ interface BatchServiceAsync { * before changing to `cancelled`, where it will have partial results (if any) available in the * output file. */ - fun cancel(params: BatchCancelParams): CompletableFuture = - cancel(params, RequestOptions.none()) + fun cancel(batchId: String): CompletableFuture = + cancel(batchId, BatchCancelParams.none()) + + /** @see [cancel] */ + fun cancel( + batchId: String, + params: BatchCancelParams = BatchCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + cancel(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [cancel] */ + fun cancel( + batchId: String, + params: BatchCancelParams = BatchCancelParams.none(), + ): CompletableFuture = cancel(batchId, params, RequestOptions.none()) /** @see [cancel] */ fun cancel( @@ -72,6 +108,14 @@ interface BatchServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [cancel] */ + fun cancel(params: BatchCancelParams): CompletableFuture = + cancel(params, RequestOptions.none()) + + /** @see [cancel] */ + fun cancel(batchId: String, requestOptions: RequestOptions): CompletableFuture = + cancel(batchId, BatchCancelParams.none(), requestOptions) + /** A view of [BatchServiceAsync] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -95,8 +139,25 @@ interface BatchServiceAsync { * [BatchServiceAsync.retrieve]. */ @MustBeClosed - fun retrieve(params: BatchRetrieveParams): CompletableFuture> = - retrieve(params, RequestOptions.none()) + fun retrieve(batchId: String): CompletableFuture> = + retrieve(batchId, BatchRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + batchId: String, + params: BatchRetrieveParams = BatchRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + batchId: String, + params: BatchRetrieveParams = BatchRetrieveParams.none(), + ): CompletableFuture> = + retrieve(batchId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -105,6 +166,19 @@ interface BatchServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: BatchRetrieveParams): CompletableFuture> = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + batchId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + retrieve(batchId, BatchRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /batches`, but is otherwise the same as * [BatchServiceAsync.list]. @@ -139,8 +213,25 @@ interface BatchServiceAsync { * same as [BatchServiceAsync.cancel]. */ @MustBeClosed - fun cancel(params: BatchCancelParams): CompletableFuture> = - cancel(params, RequestOptions.none()) + fun cancel(batchId: String): CompletableFuture> = + cancel(batchId, BatchCancelParams.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + batchId: String, + params: BatchCancelParams = BatchCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + cancel(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + batchId: String, + params: BatchCancelParams = BatchCancelParams.none(), + ): CompletableFuture> = + cancel(batchId, params, RequestOptions.none()) /** @see [cancel] */ @MustBeClosed @@ -148,5 +239,18 @@ interface BatchServiceAsync { params: BatchCancelParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + + /** @see [cancel] */ + @MustBeClosed + fun cancel(params: BatchCancelParams): CompletableFuture> = + cancel(params, RequestOptions.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + batchId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + cancel(batchId, BatchCancelParams.none(), requestOptions) } } 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 82fbc507..bcd74110 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 @@ -4,6 +4,7 @@ package com.openai.services.async import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -23,6 +24,7 @@ import com.openai.models.batches.BatchListPageResponse import com.openai.models.batches.BatchListParams import com.openai.models.batches.BatchRetrieveParams import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class BatchServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : BatchServiceAsync { @@ -103,6 +105,9 @@ class BatchServiceAsyncImpl internal constructor(private val clientOptions: Clie params: BatchRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("batchId", params.batchId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -154,6 +159,7 @@ class BatchServiceAsyncImpl internal constructor(private val clientOptions: Clie .let { BatchListPageAsync.builder() .service(BatchServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() @@ -169,6 +175,9 @@ class BatchServiceAsyncImpl internal constructor(private val clientOptions: Clie params: BatchCancelParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("batchId", params.batchId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/EvalServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/EvalServiceAsync.kt index 8c2fad82..503416d5 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/EvalServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/EvalServiceAsync.kt @@ -29,10 +29,10 @@ interface EvalServiceAsync { /** * Create the structure of an evaluation that can be used to test a model's performance. An - * evaluation is a set of testing criteria and a datasource. After creating an evaluation, you - * can run it on different models and model parameters. We support several types of graders and - * datasources. For more information, see the - * [Evals guide](https://platform.openai.com/docs/guides/evals). + * evaluation is a set of testing criteria and the config for a data source, which dictates the + * schema of the data used in the evaluation. After creating an evaluation, you can run it on + * different models and model parameters. We support several types of graders and datasources. + * For more information, see the [Evals guide](https://platform.openai.com/docs/guides/evals). */ fun create(params: EvalCreateParams): CompletableFuture = create(params, RequestOptions.none()) @@ -44,8 +44,22 @@ interface EvalServiceAsync { ): CompletableFuture /** Get an evaluation by ID. */ - fun retrieve(params: EvalRetrieveParams): CompletableFuture = - retrieve(params, RequestOptions.none()) + fun retrieve(evalId: String): CompletableFuture = + retrieve(evalId, EvalRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + evalId: String, + params: EvalRetrieveParams = EvalRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + evalId: String, + params: EvalRetrieveParams = EvalRetrieveParams.none(), + ): CompletableFuture = retrieve(evalId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -53,9 +67,34 @@ interface EvalServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [retrieve] */ + fun retrieve(params: EvalRetrieveParams): CompletableFuture = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + evalId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + retrieve(evalId, EvalRetrieveParams.none(), requestOptions) + /** Update certain properties of an evaluation. */ - fun update(params: EvalUpdateParams): CompletableFuture = - update(params, RequestOptions.none()) + fun update(evalId: String): CompletableFuture = + update(evalId, EvalUpdateParams.none()) + + /** @see [update] */ + fun update( + evalId: String, + params: EvalUpdateParams = EvalUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + update(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [update] */ + fun update( + evalId: String, + params: EvalUpdateParams = EvalUpdateParams.none(), + ): CompletableFuture = update(evalId, params, RequestOptions.none()) /** @see [update] */ fun update( @@ -63,6 +102,17 @@ interface EvalServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [update] */ + fun update(params: EvalUpdateParams): CompletableFuture = + update(params, RequestOptions.none()) + + /** @see [update] */ + fun update( + evalId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + update(evalId, EvalUpdateParams.none(), requestOptions) + /** List evaluations for a project. */ fun list(): CompletableFuture = list(EvalListParams.none()) @@ -81,8 +131,22 @@ interface EvalServiceAsync { list(EvalListParams.none(), requestOptions) /** Delete an evaluation. */ - fun delete(params: EvalDeleteParams): CompletableFuture = - delete(params, RequestOptions.none()) + fun delete(evalId: String): CompletableFuture = + delete(evalId, EvalDeleteParams.none()) + + /** @see [delete] */ + fun delete( + evalId: String, + params: EvalDeleteParams = EvalDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [delete] */ + fun delete( + evalId: String, + params: EvalDeleteParams = EvalDeleteParams.none(), + ): CompletableFuture = delete(evalId, params, RequestOptions.none()) /** @see [delete] */ fun delete( @@ -90,6 +154,17 @@ interface EvalServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [delete] */ + fun delete(params: EvalDeleteParams): CompletableFuture = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete( + evalId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + delete(evalId, EvalDeleteParams.none(), requestOptions) + /** A view of [EvalServiceAsync] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -117,10 +192,25 @@ interface EvalServiceAsync { * [EvalServiceAsync.retrieve]. */ @MustBeClosed + fun retrieve(evalId: String): CompletableFuture> = + retrieve(evalId, EvalRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve( - params: EvalRetrieveParams + evalId: String, + params: EvalRetrieveParams = EvalRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> = - retrieve(params, RequestOptions.none()) + retrieve(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + evalId: String, + params: EvalRetrieveParams = EvalRetrieveParams.none(), + ): CompletableFuture> = + retrieve(evalId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -129,15 +219,45 @@ interface EvalServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + params: EvalRetrieveParams + ): CompletableFuture> = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + evalId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + retrieve(evalId, EvalRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /evals/{eval_id}`, but is otherwise the same as * [EvalServiceAsync.update]. */ @MustBeClosed + fun update(evalId: String): CompletableFuture> = + update(evalId, EvalUpdateParams.none()) + + /** @see [update] */ + @MustBeClosed fun update( - params: EvalUpdateParams + evalId: String, + params: EvalUpdateParams = EvalUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> = - update(params, RequestOptions.none()) + update(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed + fun update( + evalId: String, + params: EvalUpdateParams = EvalUpdateParams.none(), + ): CompletableFuture> = + update(evalId, params, RequestOptions.none()) /** @see [update] */ @MustBeClosed @@ -146,6 +266,21 @@ interface EvalServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [update] */ + @MustBeClosed + fun update( + params: EvalUpdateParams + ): CompletableFuture> = + update(params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + evalId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + update(evalId, EvalUpdateParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /evals`, but is otherwise the same as * [EvalServiceAsync.list]. @@ -180,10 +315,25 @@ interface EvalServiceAsync { * [EvalServiceAsync.delete]. */ @MustBeClosed + fun delete(evalId: String): CompletableFuture> = + delete(evalId, EvalDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed fun delete( - params: EvalDeleteParams + evalId: String, + params: EvalDeleteParams = EvalDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> = - delete(params, RequestOptions.none()) + delete(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + evalId: String, + params: EvalDeleteParams = EvalDeleteParams.none(), + ): CompletableFuture> = + delete(evalId, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -191,5 +341,20 @@ interface EvalServiceAsync { params: EvalDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + + /** @see [delete] */ + @MustBeClosed + fun delete( + params: EvalDeleteParams + ): CompletableFuture> = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + evalId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + delete(evalId, EvalDeleteParams.none(), requestOptions) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/EvalServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/EvalServiceAsyncImpl.kt index 46b90a99..8ceac275 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/EvalServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/EvalServiceAsyncImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.async import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -29,6 +30,7 @@ import com.openai.models.evals.EvalUpdateResponse import com.openai.services.async.evals.RunServiceAsync import com.openai.services.async.evals.RunServiceAsyncImpl import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class EvalServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : EvalServiceAsync { @@ -127,6 +129,9 @@ class EvalServiceAsyncImpl internal constructor(private val clientOptions: Clien params: EvalRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("evalId", params.evalId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -156,6 +161,9 @@ class EvalServiceAsyncImpl internal constructor(private val clientOptions: Clien params: EvalUpdateParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("evalId", params.evalId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -208,6 +216,7 @@ class EvalServiceAsyncImpl internal constructor(private val clientOptions: Clien .let { EvalListPageAsync.builder() .service(EvalServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() @@ -223,6 +232,9 @@ class EvalServiceAsyncImpl internal constructor(private val clientOptions: Clien params: EvalDeleteParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("evalId", params.evalId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) 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 1da62fdf..6cc13c80 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 @@ -52,8 +52,22 @@ interface FileServiceAsync { ): CompletableFuture /** Returns information about a specific file. */ - fun retrieve(params: FileRetrieveParams): CompletableFuture = - retrieve(params, RequestOptions.none()) + fun retrieve(fileId: String): CompletableFuture = + retrieve(fileId, FileRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + fileId: String, + params: FileRetrieveParams = FileRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + fileId: String, + params: FileRetrieveParams = FileRetrieveParams.none(), + ): CompletableFuture = retrieve(fileId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -61,6 +75,14 @@ interface FileServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [retrieve] */ + fun retrieve(params: FileRetrieveParams): CompletableFuture = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve(fileId: String, requestOptions: RequestOptions): CompletableFuture = + retrieve(fileId, FileRetrieveParams.none(), requestOptions) + /** Returns a list of files. */ fun list(): CompletableFuture = list(FileListParams.none()) @@ -79,8 +101,22 @@ interface FileServiceAsync { list(FileListParams.none(), requestOptions) /** Delete a file. */ - fun delete(params: FileDeleteParams): CompletableFuture = - delete(params, RequestOptions.none()) + fun delete(fileId: String): CompletableFuture = + delete(fileId, FileDeleteParams.none()) + + /** @see [delete] */ + fun delete( + fileId: String, + params: FileDeleteParams = FileDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [delete] */ + fun delete( + fileId: String, + params: FileDeleteParams = FileDeleteParams.none(), + ): CompletableFuture = delete(fileId, params, RequestOptions.none()) /** @see [delete] */ fun delete( @@ -88,10 +124,34 @@ interface FileServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [delete] */ + fun delete(params: FileDeleteParams): CompletableFuture = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete(fileId: String, requestOptions: RequestOptions): CompletableFuture = + delete(fileId, FileDeleteParams.none(), requestOptions) + /** Returns the contents of the specified file. */ @MustBeClosed - fun content(params: FileContentParams): CompletableFuture = - content(params, RequestOptions.none()) + fun content(fileId: String): CompletableFuture = + content(fileId, FileContentParams.none()) + + /** @see [content] */ + @MustBeClosed + fun content( + fileId: String, + params: FileContentParams = FileContentParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + content(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [content] */ + @MustBeClosed + fun content( + fileId: String, + params: FileContentParams = FileContentParams.none(), + ): CompletableFuture = content(fileId, params, RequestOptions.none()) /** @see [content] */ @MustBeClosed @@ -100,6 +160,16 @@ interface FileServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [content] */ + @MustBeClosed + fun content(params: FileContentParams): CompletableFuture = + content(params, RequestOptions.none()) + + /** @see [content] */ + @MustBeClosed + fun content(fileId: String, requestOptions: RequestOptions): CompletableFuture = + content(fileId, FileContentParams.none(), requestOptions) + /** A view of [FileServiceAsync] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -123,8 +193,25 @@ interface FileServiceAsync { * [FileServiceAsync.retrieve]. */ @MustBeClosed - fun retrieve(params: FileRetrieveParams): CompletableFuture> = - retrieve(params, RequestOptions.none()) + fun retrieve(fileId: String): CompletableFuture> = + retrieve(fileId, FileRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fileId: String, + params: FileRetrieveParams = FileRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fileId: String, + params: FileRetrieveParams = FileRetrieveParams.none(), + ): CompletableFuture> = + retrieve(fileId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -133,6 +220,19 @@ interface FileServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: FileRetrieveParams): CompletableFuture> = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fileId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + retrieve(fileId, FileRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /files`, but is otherwise the same as * [FileServiceAsync.list]. @@ -167,8 +267,25 @@ interface FileServiceAsync { * [FileServiceAsync.delete]. */ @MustBeClosed - fun delete(params: FileDeleteParams): CompletableFuture> = - delete(params, RequestOptions.none()) + fun delete(fileId: String): CompletableFuture> = + delete(fileId, FileDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + fileId: String, + params: FileDeleteParams = FileDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + delete(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + fileId: String, + params: FileDeleteParams = FileDeleteParams.none(), + ): CompletableFuture> = + delete(fileId, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -177,13 +294,42 @@ interface FileServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [delete] */ + @MustBeClosed + fun delete(params: FileDeleteParams): CompletableFuture> = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + fileId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + delete(fileId, FileDeleteParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /files/{file_id}/content`, but is otherwise the same * as [FileServiceAsync.content]. */ @MustBeClosed - fun content(params: FileContentParams): CompletableFuture = - content(params, RequestOptions.none()) + fun content(fileId: String): CompletableFuture = + content(fileId, FileContentParams.none()) + + /** @see [content] */ + @MustBeClosed + fun content( + fileId: String, + params: FileContentParams = FileContentParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + content(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [content] */ + @MustBeClosed + fun content( + fileId: String, + params: FileContentParams = FileContentParams.none(), + ): CompletableFuture = content(fileId, params, RequestOptions.none()) /** @see [content] */ @MustBeClosed @@ -191,5 +337,18 @@ interface FileServiceAsync { params: FileContentParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** @see [content] */ + @MustBeClosed + fun content(params: FileContentParams): CompletableFuture = + content(params, RequestOptions.none()) + + /** @see [content] */ + @MustBeClosed + fun content( + fileId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + content(fileId, FileContentParams.none(), requestOptions) } } 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 a60ded87..83a8c11c 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 @@ -4,6 +4,7 @@ package com.openai.services.async import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -27,6 +28,7 @@ import com.openai.models.files.FileListParams import com.openai.models.files.FileObject import com.openai.models.files.FileRetrieveParams import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class FileServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : FileServiceAsync { @@ -114,6 +116,9 @@ class FileServiceAsyncImpl internal constructor(private val clientOptions: Clien params: FileRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fileId", params.fileId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -165,6 +170,7 @@ class FileServiceAsyncImpl internal constructor(private val clientOptions: Clien .let { FileListPageAsync.builder() .service(FileServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() @@ -180,6 +186,9 @@ class FileServiceAsyncImpl internal constructor(private val clientOptions: Clien params: FileDeleteParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fileId", params.fileId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) @@ -207,6 +216,9 @@ class FileServiceAsyncImpl internal constructor(private val clientOptions: Clien params: FileContentParams, requestOptions: RequestOptions, ): CompletableFuture { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fileId", params.fileId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) 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 aaa7bc2c..920bcfcc 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 @@ -2,8 +2,10 @@ package com.openai.services.async +import com.openai.services.async.finetuning.AlphaServiceAsync import com.openai.services.async.finetuning.CheckpointServiceAsync import com.openai.services.async.finetuning.JobServiceAsync +import com.openai.services.async.finetuning.MethodServiceAsync interface FineTuningServiceAsync { @@ -12,18 +14,26 @@ interface FineTuningServiceAsync { */ fun withRawResponse(): WithRawResponse + fun methods(): MethodServiceAsync + fun jobs(): JobServiceAsync fun checkpoints(): CheckpointServiceAsync + fun alpha(): AlphaServiceAsync + /** * A view of [FineTuningServiceAsync] that provides access to raw HTTP responses for each * method. */ interface WithRawResponse { + fun methods(): MethodServiceAsync.WithRawResponse + fun jobs(): JobServiceAsync.WithRawResponse fun checkpoints(): CheckpointServiceAsync.WithRawResponse + + fun alpha(): AlphaServiceAsync.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 ca6f7643..59413489 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 @@ -3,10 +3,14 @@ package com.openai.services.async import com.openai.core.ClientOptions +import com.openai.services.async.finetuning.AlphaServiceAsync +import com.openai.services.async.finetuning.AlphaServiceAsyncImpl import com.openai.services.async.finetuning.CheckpointServiceAsync import com.openai.services.async.finetuning.CheckpointServiceAsyncImpl import com.openai.services.async.finetuning.JobServiceAsync import com.openai.services.async.finetuning.JobServiceAsyncImpl +import com.openai.services.async.finetuning.MethodServiceAsync +import com.openai.services.async.finetuning.MethodServiceAsyncImpl class FineTuningServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : FineTuningServiceAsync { @@ -15,21 +19,33 @@ class FineTuningServiceAsyncImpl internal constructor(private val clientOptions: WithRawResponseImpl(clientOptions) } + private val methods: MethodServiceAsync by lazy { MethodServiceAsyncImpl(clientOptions) } + private val jobs: JobServiceAsync by lazy { JobServiceAsyncImpl(clientOptions) } private val checkpoints: CheckpointServiceAsync by lazy { CheckpointServiceAsyncImpl(clientOptions) } + private val alpha: AlphaServiceAsync by lazy { AlphaServiceAsyncImpl(clientOptions) } + override fun withRawResponse(): FineTuningServiceAsync.WithRawResponse = withRawResponse + override fun methods(): MethodServiceAsync = methods + override fun jobs(): JobServiceAsync = jobs override fun checkpoints(): CheckpointServiceAsync = checkpoints + override fun alpha(): AlphaServiceAsync = alpha + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : FineTuningServiceAsync.WithRawResponse { + private val methods: MethodServiceAsync.WithRawResponse by lazy { + MethodServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + private val jobs: JobServiceAsync.WithRawResponse by lazy { JobServiceAsyncImpl.WithRawResponseImpl(clientOptions) } @@ -38,8 +54,16 @@ class FineTuningServiceAsyncImpl internal constructor(private val clientOptions: CheckpointServiceAsyncImpl.WithRawResponseImpl(clientOptions) } + private val alpha: AlphaServiceAsync.WithRawResponse by lazy { + AlphaServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun methods(): MethodServiceAsync.WithRawResponse = methods + override fun jobs(): JobServiceAsync.WithRawResponse = jobs override fun checkpoints(): CheckpointServiceAsync.WithRawResponse = checkpoints + + override fun alpha(): AlphaServiceAsync.WithRawResponse = alpha } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/GraderServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/GraderServiceAsync.kt new file mode 100644 index 00000000..fdc63d85 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/GraderServiceAsync.kt @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async + +import com.openai.services.async.graders.GraderModelServiceAsync + +interface GraderServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + fun graderModels(): GraderModelServiceAsync + + /** + * A view of [GraderServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + fun graderModels(): GraderModelServiceAsync.WithRawResponse + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/GraderServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/GraderServiceAsyncImpl.kt new file mode 100644 index 00000000..28ac64f3 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/GraderServiceAsyncImpl.kt @@ -0,0 +1,33 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async + +import com.openai.core.ClientOptions +import com.openai.services.async.graders.GraderModelServiceAsync +import com.openai.services.async.graders.GraderModelServiceAsyncImpl + +class GraderServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + GraderServiceAsync { + + private val withRawResponse: GraderServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val graderModels: GraderModelServiceAsync by lazy { + GraderModelServiceAsyncImpl(clientOptions) + } + + override fun withRawResponse(): GraderServiceAsync.WithRawResponse = withRawResponse + + override fun graderModels(): GraderModelServiceAsync = graderModels + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + GraderServiceAsync.WithRawResponse { + + private val graderModels: GraderModelServiceAsync.WithRawResponse by lazy { + GraderModelServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun graderModels(): GraderModelServiceAsync.WithRawResponse = graderModels + } +} 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 33205d21..993f48db 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 @@ -24,8 +24,21 @@ interface ModelServiceAsync { * Retrieves a model instance, providing basic information about the model such as the owner and * permissioning. */ - fun retrieve(params: ModelRetrieveParams): CompletableFuture = - retrieve(params, RequestOptions.none()) + fun retrieve(model: String): CompletableFuture = + retrieve(model, ModelRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + model: String, + params: ModelRetrieveParams = ModelRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = retrieve(params.toBuilder().model(model).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + model: String, + params: ModelRetrieveParams = ModelRetrieveParams.none(), + ): CompletableFuture = retrieve(model, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -33,6 +46,14 @@ interface ModelServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [retrieve] */ + fun retrieve(params: ModelRetrieveParams): CompletableFuture = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve(model: String, requestOptions: RequestOptions): CompletableFuture = + retrieve(model, ModelRetrieveParams.none(), requestOptions) + /** * Lists the currently available models, and provides basic information about each one such as * the owner and availability. @@ -58,8 +79,22 @@ interface ModelServiceAsync { * Delete a fine-tuned model. You must have the Owner role in your organization to delete a * model. */ - fun delete(params: ModelDeleteParams): CompletableFuture = - delete(params, RequestOptions.none()) + fun delete(model: String): CompletableFuture = + delete(model, ModelDeleteParams.none()) + + /** @see [delete] */ + fun delete( + model: String, + params: ModelDeleteParams = ModelDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().model(model).build(), requestOptions) + + /** @see [delete] */ + fun delete( + model: String, + params: ModelDeleteParams = ModelDeleteParams.none(), + ): CompletableFuture = delete(model, params, RequestOptions.none()) /** @see [delete] */ fun delete( @@ -67,6 +102,14 @@ interface ModelServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [delete] */ + fun delete(params: ModelDeleteParams): CompletableFuture = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete(model: String, requestOptions: RequestOptions): CompletableFuture = + delete(model, ModelDeleteParams.none(), requestOptions) + /** A view of [ModelServiceAsync] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -75,8 +118,25 @@ interface ModelServiceAsync { * [ModelServiceAsync.retrieve]. */ @MustBeClosed - fun retrieve(params: ModelRetrieveParams): CompletableFuture> = - retrieve(params, RequestOptions.none()) + fun retrieve(model: String): CompletableFuture> = + retrieve(model, ModelRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + model: String, + params: ModelRetrieveParams = ModelRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().model(model).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + model: String, + params: ModelRetrieveParams = ModelRetrieveParams.none(), + ): CompletableFuture> = + retrieve(model, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -85,6 +145,19 @@ interface ModelServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: ModelRetrieveParams): CompletableFuture> = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + model: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + retrieve(model, ModelRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /models`, but is otherwise the same as * [ModelServiceAsync.list]. @@ -119,8 +192,25 @@ interface ModelServiceAsync { * [ModelServiceAsync.delete]. */ @MustBeClosed - fun delete(params: ModelDeleteParams): CompletableFuture> = - delete(params, RequestOptions.none()) + fun delete(model: String): CompletableFuture> = + delete(model, ModelDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + model: String, + params: ModelDeleteParams = ModelDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + delete(params.toBuilder().model(model).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + model: String, + params: ModelDeleteParams = ModelDeleteParams.none(), + ): CompletableFuture> = + delete(model, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -128,5 +218,18 @@ interface ModelServiceAsync { params: ModelDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + + /** @see [delete] */ + @MustBeClosed + fun delete(params: ModelDeleteParams): CompletableFuture> = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + model: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + delete(model, ModelDeleteParams.none(), requestOptions) } } 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 7973eaa3..9f15cdea 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 @@ -4,6 +4,7 @@ package com.openai.services.async import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -23,6 +24,7 @@ import com.openai.models.models.ModelListPageResponse import com.openai.models.models.ModelListParams import com.openai.models.models.ModelRetrieveParams import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class ModelServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : ModelServiceAsync { @@ -66,12 +68,15 @@ class ModelServiceAsyncImpl internal constructor(private val clientOptions: Clie params: ModelRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("model", params.model().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) .addPathSegments("models", params._pathParam(0)) .build() - .prepareAsync(clientOptions, params, params.model()) + .prepareAsync(clientOptions, params, params.model().get()) val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) return request .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } @@ -117,6 +122,7 @@ class ModelServiceAsyncImpl internal constructor(private val clientOptions: Clie .let { ModelListPageAsync.builder() .service(ModelServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() @@ -132,13 +138,16 @@ class ModelServiceAsyncImpl internal constructor(private val clientOptions: Clie params: ModelDeleteParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("model", params.model().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) .addPathSegments("models", params._pathParam(0)) .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } .build() - .prepareAsync(clientOptions, params, params.model()) + .prepareAsync(clientOptions, params, params.model().get()) 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/ResponseServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/ResponseServiceAsync.kt index 34042fd2..f66149de 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/ResponseServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/ResponseServiceAsync.kt @@ -66,8 +66,22 @@ interface ResponseServiceAsync { ): AsyncStreamResponse /** Retrieves a model response with the given ID. */ - fun retrieve(params: ResponseRetrieveParams): CompletableFuture = - retrieve(params, RequestOptions.none()) + fun retrieve(responseId: String): CompletableFuture = + retrieve(responseId, ResponseRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + responseId: String, + params: ResponseRetrieveParams = ResponseRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().responseId(responseId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + responseId: String, + params: ResponseRetrieveParams = ResponseRetrieveParams.none(), + ): CompletableFuture = retrieve(responseId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -75,9 +89,31 @@ interface ResponseServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [retrieve] */ + fun retrieve(params: ResponseRetrieveParams): CompletableFuture = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve(responseId: String, requestOptions: RequestOptions): CompletableFuture = + retrieve(responseId, ResponseRetrieveParams.none(), requestOptions) + /** Deletes a model response with the given ID. */ - fun delete(params: ResponseDeleteParams): CompletableFuture = - delete(params, RequestOptions.none()) + fun delete(responseId: String): CompletableFuture = + delete(responseId, ResponseDeleteParams.none()) + + /** @see [delete] */ + fun delete( + responseId: String, + params: ResponseDeleteParams = ResponseDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().responseId(responseId).build(), requestOptions) + + /** @see [delete] */ + fun delete( + responseId: String, + params: ResponseDeleteParams = ResponseDeleteParams.none(), + ): CompletableFuture = delete(responseId, params, RequestOptions.none()) /** @see [delete] */ fun delete( @@ -85,6 +121,14 @@ interface ResponseServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [delete] */ + fun delete(params: ResponseDeleteParams): CompletableFuture = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete(responseId: String, requestOptions: RequestOptions): CompletableFuture = + delete(responseId, ResponseDeleteParams.none(), requestOptions) + /** * A view of [ResponseServiceAsync] that provides access to raw HTTP responses for each method. */ @@ -129,8 +173,25 @@ interface ResponseServiceAsync { * as [ResponseServiceAsync.retrieve]. */ @MustBeClosed - fun retrieve(params: ResponseRetrieveParams): CompletableFuture> = - retrieve(params, RequestOptions.none()) + fun retrieve(responseId: String): CompletableFuture> = + retrieve(responseId, ResponseRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + responseId: String, + params: ResponseRetrieveParams = ResponseRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().responseId(responseId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + responseId: String, + params: ResponseRetrieveParams = ResponseRetrieveParams.none(), + ): CompletableFuture> = + retrieve(responseId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -139,13 +200,42 @@ interface ResponseServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: ResponseRetrieveParams): CompletableFuture> = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + responseId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + retrieve(responseId, ResponseRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `delete /responses/{response_id}`, but is otherwise the * same as [ResponseServiceAsync.delete]. */ @MustBeClosed - fun delete(params: ResponseDeleteParams): CompletableFuture = - delete(params, RequestOptions.none()) + fun delete(responseId: String): CompletableFuture = + delete(responseId, ResponseDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + responseId: String, + params: ResponseDeleteParams = ResponseDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().responseId(responseId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + responseId: String, + params: ResponseDeleteParams = ResponseDeleteParams.none(), + ): CompletableFuture = delete(responseId, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -153,5 +243,18 @@ interface ResponseServiceAsync { params: ResponseDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + + /** @see [delete] */ + @MustBeClosed + fun delete(params: ResponseDeleteParams): CompletableFuture = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + responseId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + delete(responseId, ResponseDeleteParams.none(), requestOptions) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/ResponseServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/ResponseServiceAsyncImpl.kt index 122e68f4..7f90cea5 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/ResponseServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/ResponseServiceAsyncImpl.kt @@ -5,6 +5,7 @@ package com.openai.services.async import com.openai.core.ClientOptions import com.openai.core.JsonValue import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.emptyHandler import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler @@ -32,6 +33,7 @@ import com.openai.models.responses.ResponseStreamEvent import com.openai.services.async.responses.InputItemServiceAsync import com.openai.services.async.responses.InputItemServiceAsyncImpl import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class ResponseServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : ResponseServiceAsync { @@ -170,6 +172,9 @@ class ResponseServiceAsyncImpl internal constructor(private val clientOptions: C params: ResponseRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("responseId", params.responseId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -198,6 +203,9 @@ class ResponseServiceAsyncImpl internal constructor(private val clientOptions: C params: ResponseDeleteParams, requestOptions: RequestOptions, ): CompletableFuture { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("responseId", params.responseId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) 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 7e0dce0c..98218ac5 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 @@ -51,8 +51,22 @@ interface UploadServiceAsync { ): CompletableFuture /** Cancels the Upload. No Parts may be added after an Upload is cancelled. */ - fun cancel(params: UploadCancelParams): CompletableFuture = - cancel(params, RequestOptions.none()) + fun cancel(uploadId: String): CompletableFuture = + cancel(uploadId, UploadCancelParams.none()) + + /** @see [cancel] */ + fun cancel( + uploadId: String, + params: UploadCancelParams = UploadCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + cancel(params.toBuilder().uploadId(uploadId).build(), requestOptions) + + /** @see [cancel] */ + fun cancel( + uploadId: String, + params: UploadCancelParams = UploadCancelParams.none(), + ): CompletableFuture = cancel(uploadId, params, RequestOptions.none()) /** @see [cancel] */ fun cancel( @@ -60,6 +74,14 @@ interface UploadServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [cancel] */ + fun cancel(params: UploadCancelParams): CompletableFuture = + cancel(params, RequestOptions.none()) + + /** @see [cancel] */ + fun cancel(uploadId: String, requestOptions: RequestOptions): CompletableFuture = + cancel(uploadId, UploadCancelParams.none(), requestOptions) + /** * Completes the [Upload](https://platform.openai.com/docs/api-reference/uploads/object). * @@ -73,6 +95,18 @@ interface UploadServiceAsync { * specified when creating the Upload object. No Parts may be added after an Upload is * completed. */ + fun complete(uploadId: String, params: UploadCompleteParams): CompletableFuture = + complete(uploadId, params, RequestOptions.none()) + + /** @see [complete] */ + fun complete( + uploadId: String, + params: UploadCompleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + complete(params.toBuilder().uploadId(uploadId).build(), requestOptions) + + /** @see [complete] */ fun complete(params: UploadCompleteParams): CompletableFuture = complete(params, RequestOptions.none()) @@ -109,8 +143,25 @@ interface UploadServiceAsync { * same as [UploadServiceAsync.cancel]. */ @MustBeClosed - fun cancel(params: UploadCancelParams): CompletableFuture> = - cancel(params, RequestOptions.none()) + fun cancel(uploadId: String): CompletableFuture> = + cancel(uploadId, UploadCancelParams.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + uploadId: String, + params: UploadCancelParams = UploadCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + cancel(params.toBuilder().uploadId(uploadId).build(), requestOptions) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + uploadId: String, + params: UploadCancelParams = UploadCancelParams.none(), + ): CompletableFuture> = + cancel(uploadId, params, RequestOptions.none()) /** @see [cancel] */ @MustBeClosed @@ -119,11 +170,41 @@ interface UploadServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [cancel] */ + @MustBeClosed + fun cancel(params: UploadCancelParams): CompletableFuture> = + cancel(params, RequestOptions.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + uploadId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + cancel(uploadId, UploadCancelParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /uploads/{upload_id}/complete`, but is otherwise * the same as [UploadServiceAsync.complete]. */ @MustBeClosed + fun complete( + uploadId: String, + params: UploadCompleteParams, + ): CompletableFuture> = + complete(uploadId, params, RequestOptions.none()) + + /** @see [complete] */ + @MustBeClosed + fun complete( + uploadId: String, + params: UploadCompleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + complete(params.toBuilder().uploadId(uploadId).build(), requestOptions) + + /** @see [complete] */ + @MustBeClosed fun complete(params: UploadCompleteParams): CompletableFuture> = complete(params, RequestOptions.none()) 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 9877742d..30cc034a 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 @@ -4,6 +4,7 @@ package com.openai.services.async import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -22,6 +23,7 @@ import com.openai.models.uploads.UploadCreateParams import com.openai.services.async.uploads.PartServiceAsync import com.openai.services.async.uploads.PartServiceAsyncImpl import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class UploadServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : UploadServiceAsync { @@ -105,6 +107,9 @@ class UploadServiceAsyncImpl internal constructor(private val clientOptions: Cli params: UploadCancelParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("uploadId", params.uploadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -135,6 +140,9 @@ class UploadServiceAsyncImpl internal constructor(private val clientOptions: Cli params: UploadCompleteParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("uploadId", params.uploadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/VectorStoreServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/VectorStoreServiceAsync.kt index 4fb10a20..ff07354c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/VectorStoreServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/VectorStoreServiceAsync.kt @@ -49,8 +49,22 @@ interface VectorStoreServiceAsync { create(VectorStoreCreateParams.none(), requestOptions) /** Retrieves a vector store. */ - fun retrieve(params: VectorStoreRetrieveParams): CompletableFuture = - retrieve(params, RequestOptions.none()) + fun retrieve(vectorStoreId: String): CompletableFuture = + retrieve(vectorStoreId, VectorStoreRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + vectorStoreId: String, + params: VectorStoreRetrieveParams = VectorStoreRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + vectorStoreId: String, + params: VectorStoreRetrieveParams = VectorStoreRetrieveParams.none(), + ): CompletableFuture = retrieve(vectorStoreId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -58,9 +72,34 @@ interface VectorStoreServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [retrieve] */ + fun retrieve(params: VectorStoreRetrieveParams): CompletableFuture = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + vectorStoreId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + retrieve(vectorStoreId, VectorStoreRetrieveParams.none(), requestOptions) + /** Modifies a vector store. */ - fun update(params: VectorStoreUpdateParams): CompletableFuture = - update(params, RequestOptions.none()) + fun update(vectorStoreId: String): CompletableFuture = + update(vectorStoreId, VectorStoreUpdateParams.none()) + + /** @see [update] */ + fun update( + vectorStoreId: String, + params: VectorStoreUpdateParams = VectorStoreUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + update(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [update] */ + fun update( + vectorStoreId: String, + params: VectorStoreUpdateParams = VectorStoreUpdateParams.none(), + ): CompletableFuture = update(vectorStoreId, params, RequestOptions.none()) /** @see [update] */ fun update( @@ -68,6 +107,17 @@ interface VectorStoreServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [update] */ + fun update(params: VectorStoreUpdateParams): CompletableFuture = + update(params, RequestOptions.none()) + + /** @see [update] */ + fun update( + vectorStoreId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + update(vectorStoreId, VectorStoreUpdateParams.none(), requestOptions) + /** Returns a list of vector stores. */ fun list(): CompletableFuture = list(VectorStoreListParams.none()) @@ -87,8 +137,22 @@ interface VectorStoreServiceAsync { list(VectorStoreListParams.none(), requestOptions) /** Delete a vector store. */ - fun delete(params: VectorStoreDeleteParams): CompletableFuture = - delete(params, RequestOptions.none()) + fun delete(vectorStoreId: String): CompletableFuture = + delete(vectorStoreId, VectorStoreDeleteParams.none()) + + /** @see [delete] */ + fun delete( + vectorStoreId: String, + params: VectorStoreDeleteParams = VectorStoreDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [delete] */ + fun delete( + vectorStoreId: String, + params: VectorStoreDeleteParams = VectorStoreDeleteParams.none(), + ): CompletableFuture = delete(vectorStoreId, params, RequestOptions.none()) /** @see [delete] */ fun delete( @@ -96,7 +160,33 @@ interface VectorStoreServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [delete] */ + fun delete(params: VectorStoreDeleteParams): CompletableFuture = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete( + vectorStoreId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + delete(vectorStoreId, VectorStoreDeleteParams.none(), requestOptions) + /** Search a vector store for relevant chunks based on a query and file attributes filter. */ + fun search( + vectorStoreId: String, + params: VectorStoreSearchParams, + ): CompletableFuture = + search(vectorStoreId, params, RequestOptions.none()) + + /** @see [search] */ + fun search( + vectorStoreId: String, + params: VectorStoreSearchParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + search(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [search] */ fun search(params: VectorStoreSearchParams): CompletableFuture = search(params, RequestOptions.none()) @@ -149,9 +239,25 @@ interface VectorStoreServiceAsync { * the same as [VectorStoreServiceAsync.retrieve]. */ @MustBeClosed + fun retrieve(vectorStoreId: String): CompletableFuture> = + retrieve(vectorStoreId, VectorStoreRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve( - params: VectorStoreRetrieveParams - ): CompletableFuture> = retrieve(params, RequestOptions.none()) + vectorStoreId: String, + params: VectorStoreRetrieveParams = VectorStoreRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + vectorStoreId: String, + params: VectorStoreRetrieveParams = VectorStoreRetrieveParams.none(), + ): CompletableFuture> = + retrieve(vectorStoreId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -160,14 +266,44 @@ interface VectorStoreServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + params: VectorStoreRetrieveParams + ): CompletableFuture> = retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + vectorStoreId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + retrieve(vectorStoreId, VectorStoreRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /vector_stores/{vector_store_id}`, but is otherwise * the same as [VectorStoreServiceAsync.update]. */ @MustBeClosed + fun update(vectorStoreId: String): CompletableFuture> = + update(vectorStoreId, VectorStoreUpdateParams.none()) + + /** @see [update] */ + @MustBeClosed fun update( - params: VectorStoreUpdateParams - ): CompletableFuture> = update(params, RequestOptions.none()) + vectorStoreId: String, + params: VectorStoreUpdateParams = VectorStoreUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + update(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed + fun update( + vectorStoreId: String, + params: VectorStoreUpdateParams = VectorStoreUpdateParams.none(), + ): CompletableFuture> = + update(vectorStoreId, params, RequestOptions.none()) /** @see [update] */ @MustBeClosed @@ -176,6 +312,20 @@ interface VectorStoreServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [update] */ + @MustBeClosed + fun update( + params: VectorStoreUpdateParams + ): CompletableFuture> = update(params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + vectorStoreId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + update(vectorStoreId, VectorStoreUpdateParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /vector_stores`, but is otherwise the same as * [VectorStoreServiceAsync.list]. @@ -210,10 +360,25 @@ interface VectorStoreServiceAsync { * otherwise the same as [VectorStoreServiceAsync.delete]. */ @MustBeClosed + fun delete(vectorStoreId: String): CompletableFuture> = + delete(vectorStoreId, VectorStoreDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed fun delete( - params: VectorStoreDeleteParams + vectorStoreId: String, + params: VectorStoreDeleteParams = VectorStoreDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> = - delete(params, RequestOptions.none()) + delete(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + vectorStoreId: String, + params: VectorStoreDeleteParams = VectorStoreDeleteParams.none(), + ): CompletableFuture> = + delete(vectorStoreId, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -222,11 +387,43 @@ interface VectorStoreServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [delete] */ + @MustBeClosed + fun delete( + params: VectorStoreDeleteParams + ): CompletableFuture> = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + vectorStoreId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + delete(vectorStoreId, VectorStoreDeleteParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /vector_stores/{vector_store_id}/search`, but is * otherwise the same as [VectorStoreServiceAsync.search]. */ @MustBeClosed + fun search( + vectorStoreId: String, + params: VectorStoreSearchParams, + ): CompletableFuture> = + search(vectorStoreId, params, RequestOptions.none()) + + /** @see [search] */ + @MustBeClosed + fun search( + vectorStoreId: String, + params: VectorStoreSearchParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + search(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [search] */ + @MustBeClosed fun search( params: VectorStoreSearchParams ): CompletableFuture> = diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/VectorStoreServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/VectorStoreServiceAsyncImpl.kt index da248f22..346bb073 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/VectorStoreServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/VectorStoreServiceAsyncImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.async import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -33,6 +34,7 @@ import com.openai.services.async.vectorstores.FileBatchServiceAsyncImpl import com.openai.services.async.vectorstores.FileServiceAsync import com.openai.services.async.vectorstores.FileServiceAsyncImpl import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class VectorStoreServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : VectorStoreServiceAsync { @@ -155,6 +157,9 @@ class VectorStoreServiceAsyncImpl internal constructor(private val clientOptions params: VectorStoreRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("vectorStoreId", params.vectorStoreId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -185,6 +190,9 @@ class VectorStoreServiceAsyncImpl internal constructor(private val clientOptions params: VectorStoreUpdateParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("vectorStoreId", params.vectorStoreId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -239,6 +247,7 @@ class VectorStoreServiceAsyncImpl internal constructor(private val clientOptions .let { VectorStoreListPageAsync.builder() .service(VectorStoreServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() @@ -254,6 +263,9 @@ class VectorStoreServiceAsyncImpl internal constructor(private val clientOptions params: VectorStoreDeleteParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("vectorStoreId", params.vectorStoreId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) @@ -286,6 +298,9 @@ class VectorStoreServiceAsyncImpl internal constructor(private val clientOptions params: VectorStoreSearchParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("vectorStoreId", params.vectorStoreId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -309,6 +324,7 @@ class VectorStoreServiceAsyncImpl internal constructor(private val clientOptions .let { VectorStoreSearchPageAsync.builder() .service(VectorStoreServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() 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 84e33a14..b36485ed 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 @@ -33,8 +33,22 @@ interface AssistantServiceAsync { ): CompletableFuture /** Retrieves an assistant. */ - fun retrieve(params: AssistantRetrieveParams): CompletableFuture = - retrieve(params, RequestOptions.none()) + fun retrieve(assistantId: String): CompletableFuture = + retrieve(assistantId, AssistantRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + assistantId: String, + params: AssistantRetrieveParams = AssistantRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().assistantId(assistantId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + assistantId: String, + params: AssistantRetrieveParams = AssistantRetrieveParams.none(), + ): CompletableFuture = retrieve(assistantId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -42,9 +56,34 @@ interface AssistantServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [retrieve] */ + fun retrieve(params: AssistantRetrieveParams): CompletableFuture = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + assistantId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + retrieve(assistantId, AssistantRetrieveParams.none(), requestOptions) + /** Modifies an assistant. */ - fun update(params: AssistantUpdateParams): CompletableFuture = - update(params, RequestOptions.none()) + fun update(assistantId: String): CompletableFuture = + update(assistantId, AssistantUpdateParams.none()) + + /** @see [update] */ + fun update( + assistantId: String, + params: AssistantUpdateParams = AssistantUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + update(params.toBuilder().assistantId(assistantId).build(), requestOptions) + + /** @see [update] */ + fun update( + assistantId: String, + params: AssistantUpdateParams = AssistantUpdateParams.none(), + ): CompletableFuture = update(assistantId, params, RequestOptions.none()) /** @see [update] */ fun update( @@ -52,6 +91,14 @@ interface AssistantServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [update] */ + fun update(params: AssistantUpdateParams): CompletableFuture = + update(params, RequestOptions.none()) + + /** @see [update] */ + fun update(assistantId: String, requestOptions: RequestOptions): CompletableFuture = + update(assistantId, AssistantUpdateParams.none(), requestOptions) + /** Returns a list of assistants. */ fun list(): CompletableFuture = list(AssistantListParams.none()) @@ -71,8 +118,22 @@ interface AssistantServiceAsync { list(AssistantListParams.none(), requestOptions) /** Delete an assistant. */ - fun delete(params: AssistantDeleteParams): CompletableFuture = - delete(params, RequestOptions.none()) + fun delete(assistantId: String): CompletableFuture = + delete(assistantId, AssistantDeleteParams.none()) + + /** @see [delete] */ + fun delete( + assistantId: String, + params: AssistantDeleteParams = AssistantDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().assistantId(assistantId).build(), requestOptions) + + /** @see [delete] */ + fun delete( + assistantId: String, + params: AssistantDeleteParams = AssistantDeleteParams.none(), + ): CompletableFuture = delete(assistantId, params, RequestOptions.none()) /** @see [delete] */ fun delete( @@ -80,6 +141,17 @@ interface AssistantServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [delete] */ + fun delete(params: AssistantDeleteParams): CompletableFuture = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete( + assistantId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + delete(assistantId, AssistantDeleteParams.none(), requestOptions) + /** * A view of [AssistantServiceAsync] that provides access to raw HTTP responses for each method. */ @@ -105,9 +177,25 @@ interface AssistantServiceAsync { * same as [AssistantServiceAsync.retrieve]. */ @MustBeClosed + fun retrieve(assistantId: String): CompletableFuture> = + retrieve(assistantId, AssistantRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve( - params: AssistantRetrieveParams - ): CompletableFuture> = retrieve(params, RequestOptions.none()) + assistantId: String, + params: AssistantRetrieveParams = AssistantRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().assistantId(assistantId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + assistantId: String, + params: AssistantRetrieveParams = AssistantRetrieveParams.none(), + ): CompletableFuture> = + retrieve(assistantId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -116,13 +204,44 @@ interface AssistantServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + params: AssistantRetrieveParams + ): CompletableFuture> = retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + assistantId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + retrieve(assistantId, AssistantRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /assistants/{assistant_id}`, but is otherwise the * same as [AssistantServiceAsync.update]. */ @MustBeClosed - fun update(params: AssistantUpdateParams): CompletableFuture> = - update(params, RequestOptions.none()) + fun update(assistantId: String): CompletableFuture> = + update(assistantId, AssistantUpdateParams.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + assistantId: String, + params: AssistantUpdateParams = AssistantUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + update(params.toBuilder().assistantId(assistantId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed + fun update( + assistantId: String, + params: AssistantUpdateParams = AssistantUpdateParams.none(), + ): CompletableFuture> = + update(assistantId, params, RequestOptions.none()) /** @see [update] */ @MustBeClosed @@ -131,6 +250,19 @@ interface AssistantServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [update] */ + @MustBeClosed + fun update(params: AssistantUpdateParams): CompletableFuture> = + update(params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + assistantId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + update(assistantId, AssistantUpdateParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /assistants`, but is otherwise the same as * [AssistantServiceAsync.list]. @@ -165,10 +297,25 @@ interface AssistantServiceAsync { * same as [AssistantServiceAsync.delete]. */ @MustBeClosed + fun delete(assistantId: String): CompletableFuture> = + delete(assistantId, AssistantDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed fun delete( - params: AssistantDeleteParams + assistantId: String, + params: AssistantDeleteParams = AssistantDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> = - delete(params, RequestOptions.none()) + delete(params.toBuilder().assistantId(assistantId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + assistantId: String, + params: AssistantDeleteParams = AssistantDeleteParams.none(), + ): CompletableFuture> = + delete(assistantId, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -176,5 +323,20 @@ interface AssistantServiceAsync { params: AssistantDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + + /** @see [delete] */ + @MustBeClosed + fun delete( + params: AssistantDeleteParams + ): CompletableFuture> = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + assistantId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + delete(assistantId, AssistantDeleteParams.none(), requestOptions) } } 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 7eb20393..d7d746f2 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 @@ -4,6 +4,7 @@ package com.openai.services.async.beta import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -26,6 +27,7 @@ import com.openai.models.beta.assistants.AssistantListParams import com.openai.models.beta.assistants.AssistantRetrieveParams import com.openai.models.beta.assistants.AssistantUpdateParams import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class AssistantServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : AssistantServiceAsync { @@ -119,6 +121,9 @@ class AssistantServiceAsyncImpl internal constructor(private val clientOptions: params: AssistantRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("assistantId", params.assistantId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -149,6 +154,9 @@ class AssistantServiceAsyncImpl internal constructor(private val clientOptions: params: AssistantUpdateParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("assistantId", params.assistantId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -207,6 +215,7 @@ class AssistantServiceAsyncImpl internal constructor(private val clientOptions: .let { AssistantListPageAsync.builder() .service(AssistantServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() @@ -222,6 +231,9 @@ class AssistantServiceAsyncImpl internal constructor(private val clientOptions: params: AssistantDeleteParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("assistantId", params.assistantId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) 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 76a89f63..5eaf8642 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 @@ -49,8 +49,22 @@ interface ThreadServiceAsync { create(ThreadCreateParams.none(), requestOptions) /** Retrieves a thread. */ - fun retrieve(params: ThreadRetrieveParams): CompletableFuture = - retrieve(params, RequestOptions.none()) + fun retrieve(threadId: String): CompletableFuture = + retrieve(threadId, ThreadRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + threadId: String, + params: ThreadRetrieveParams = ThreadRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + threadId: String, + params: ThreadRetrieveParams = ThreadRetrieveParams.none(), + ): CompletableFuture = retrieve(threadId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -58,9 +72,31 @@ interface ThreadServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [retrieve] */ + fun retrieve(params: ThreadRetrieveParams): CompletableFuture = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve(threadId: String, requestOptions: RequestOptions): CompletableFuture = + retrieve(threadId, ThreadRetrieveParams.none(), requestOptions) + /** Modifies a thread. */ - fun update(params: ThreadUpdateParams): CompletableFuture = - update(params, RequestOptions.none()) + fun update(threadId: String): CompletableFuture = + update(threadId, ThreadUpdateParams.none()) + + /** @see [update] */ + fun update( + threadId: String, + params: ThreadUpdateParams = ThreadUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + update(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [update] */ + fun update( + threadId: String, + params: ThreadUpdateParams = ThreadUpdateParams.none(), + ): CompletableFuture = update(threadId, params, RequestOptions.none()) /** @see [update] */ fun update( @@ -68,9 +104,31 @@ interface ThreadServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [update] */ + fun update(params: ThreadUpdateParams): CompletableFuture = + update(params, RequestOptions.none()) + + /** @see [update] */ + fun update(threadId: String, requestOptions: RequestOptions): CompletableFuture = + update(threadId, ThreadUpdateParams.none(), requestOptions) + /** Delete a thread. */ - fun delete(params: ThreadDeleteParams): CompletableFuture = - delete(params, RequestOptions.none()) + fun delete(threadId: String): CompletableFuture = + delete(threadId, ThreadDeleteParams.none()) + + /** @see [delete] */ + fun delete( + threadId: String, + params: ThreadDeleteParams = ThreadDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [delete] */ + fun delete( + threadId: String, + params: ThreadDeleteParams = ThreadDeleteParams.none(), + ): CompletableFuture = delete(threadId, params, RequestOptions.none()) /** @see [delete] */ fun delete( @@ -78,6 +136,14 @@ interface ThreadServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [delete] */ + fun delete(params: ThreadDeleteParams): CompletableFuture = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete(threadId: String, requestOptions: RequestOptions): CompletableFuture = + delete(threadId, ThreadDeleteParams.none(), requestOptions) + /** Create a thread and run it in one request. */ fun createAndRun(params: ThreadCreateAndRunParams): CompletableFuture = createAndRun(params, RequestOptions.none()) @@ -139,8 +205,25 @@ interface ThreadServiceAsync { * [ThreadServiceAsync.retrieve]. */ @MustBeClosed - fun retrieve(params: ThreadRetrieveParams): CompletableFuture> = - retrieve(params, RequestOptions.none()) + fun retrieve(threadId: String): CompletableFuture> = + retrieve(threadId, ThreadRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + threadId: String, + params: ThreadRetrieveParams = ThreadRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + threadId: String, + params: ThreadRetrieveParams = ThreadRetrieveParams.none(), + ): CompletableFuture> = + retrieve(threadId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -149,13 +232,43 @@ interface ThreadServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: ThreadRetrieveParams): CompletableFuture> = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + threadId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + retrieve(threadId, ThreadRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /threads/{thread_id}`, but is otherwise the same as * [ThreadServiceAsync.update]. */ @MustBeClosed - fun update(params: ThreadUpdateParams): CompletableFuture> = - update(params, RequestOptions.none()) + fun update(threadId: String): CompletableFuture> = + update(threadId, ThreadUpdateParams.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + threadId: String, + params: ThreadUpdateParams = ThreadUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + update(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed + fun update( + threadId: String, + params: ThreadUpdateParams = ThreadUpdateParams.none(), + ): CompletableFuture> = + update(threadId, params, RequestOptions.none()) /** @see [update] */ @MustBeClosed @@ -164,13 +277,43 @@ interface ThreadServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [update] */ + @MustBeClosed + fun update(params: ThreadUpdateParams): CompletableFuture> = + update(params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + threadId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + update(threadId, ThreadUpdateParams.none(), requestOptions) + /** * Returns a raw HTTP response for `delete /threads/{thread_id}`, but is otherwise the same * as [ThreadServiceAsync.delete]. */ @MustBeClosed - fun delete(params: ThreadDeleteParams): CompletableFuture> = - delete(params, RequestOptions.none()) + fun delete(threadId: String): CompletableFuture> = + delete(threadId, ThreadDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + threadId: String, + params: ThreadDeleteParams = ThreadDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + delete(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + threadId: String, + params: ThreadDeleteParams = ThreadDeleteParams.none(), + ): CompletableFuture> = + delete(threadId, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -179,6 +322,19 @@ interface ThreadServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [delete] */ + @MustBeClosed + fun delete(params: ThreadDeleteParams): CompletableFuture> = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + threadId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + delete(threadId, ThreadDeleteParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /threads/runs`, but is otherwise the same as * [ThreadServiceAsync.createAndRun]. 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 c8334900..a0193523 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 @@ -5,6 +5,7 @@ package com.openai.services.async.beta import com.openai.core.ClientOptions import com.openai.core.JsonValue import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.mapJson @@ -37,6 +38,7 @@ import com.openai.services.async.beta.threads.MessageServiceAsyncImpl import com.openai.services.async.beta.threads.RunServiceAsync import com.openai.services.async.beta.threads.RunServiceAsyncImpl import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class ThreadServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : ThreadServiceAsync { @@ -160,6 +162,9 @@ class ThreadServiceAsyncImpl internal constructor(private val clientOptions: Cli params: ThreadRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -190,6 +195,9 @@ class ThreadServiceAsyncImpl internal constructor(private val clientOptions: Cli params: ThreadUpdateParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -221,6 +229,9 @@ class ThreadServiceAsyncImpl internal constructor(private val clientOptions: Cli params: ThreadDeleteParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) 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 364851ad..4ec7a151 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 @@ -23,6 +23,18 @@ interface MessageServiceAsync { fun withRawResponse(): WithRawResponse /** Create a message. */ + fun create(threadId: String, params: MessageCreateParams): CompletableFuture = + create(threadId, params, RequestOptions.none()) + + /** @see [create] */ + fun create( + threadId: String, + params: MessageCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + create(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [create] */ fun create(params: MessageCreateParams): CompletableFuture = create(params, RequestOptions.none()) @@ -33,6 +45,18 @@ interface MessageServiceAsync { ): CompletableFuture /** Retrieve a message. */ + fun retrieve(messageId: String, params: MessageRetrieveParams): CompletableFuture = + retrieve(messageId, params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + messageId: String, + params: MessageRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().messageId(messageId).build(), requestOptions) + + /** @see [retrieve] */ fun retrieve(params: MessageRetrieveParams): CompletableFuture = retrieve(params, RequestOptions.none()) @@ -43,6 +67,18 @@ interface MessageServiceAsync { ): CompletableFuture /** Modifies a message. */ + fun update(messageId: String, params: MessageUpdateParams): CompletableFuture = + update(messageId, params, RequestOptions.none()) + + /** @see [update] */ + fun update( + messageId: String, + params: MessageUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + update(params.toBuilder().messageId(messageId).build(), requestOptions) + + /** @see [update] */ fun update(params: MessageUpdateParams): CompletableFuture = update(params, RequestOptions.none()) @@ -53,8 +89,22 @@ interface MessageServiceAsync { ): CompletableFuture /** Returns a list of messages for a given thread. */ - fun list(params: MessageListParams): CompletableFuture = - list(params, RequestOptions.none()) + fun list(threadId: String): CompletableFuture = + list(threadId, MessageListParams.none()) + + /** @see [list] */ + fun list( + threadId: String, + params: MessageListParams = MessageListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + list(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [list] */ + fun list( + threadId: String, + params: MessageListParams = MessageListParams.none(), + ): CompletableFuture = list(threadId, params, RequestOptions.none()) /** @see [list] */ fun list( @@ -62,7 +112,30 @@ interface MessageServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [list] */ + fun list(params: MessageListParams): CompletableFuture = + list(params, RequestOptions.none()) + + /** @see [list] */ + fun list( + threadId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + list(threadId, MessageListParams.none(), requestOptions) + /** Deletes a message. */ + fun delete(messageId: String, params: MessageDeleteParams): CompletableFuture = + delete(messageId, params, RequestOptions.none()) + + /** @see [delete] */ + fun delete( + messageId: String, + params: MessageDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().messageId(messageId).build(), requestOptions) + + /** @see [delete] */ fun delete(params: MessageDeleteParams): CompletableFuture = delete(params, RequestOptions.none()) @@ -82,6 +155,23 @@ interface MessageServiceAsync { * the same as [MessageServiceAsync.create]. */ @MustBeClosed + fun create( + threadId: String, + params: MessageCreateParams, + ): CompletableFuture> = + create(threadId, params, RequestOptions.none()) + + /** @see [create] */ + @MustBeClosed + fun create( + threadId: String, + params: MessageCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + create(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [create] */ + @MustBeClosed fun create(params: MessageCreateParams): CompletableFuture> = create(params, RequestOptions.none()) @@ -97,6 +187,23 @@ interface MessageServiceAsync { * otherwise the same as [MessageServiceAsync.retrieve]. */ @MustBeClosed + fun retrieve( + messageId: String, + params: MessageRetrieveParams, + ): CompletableFuture> = + retrieve(messageId, params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + messageId: String, + params: MessageRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().messageId(messageId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve(params: MessageRetrieveParams): CompletableFuture> = retrieve(params, RequestOptions.none()) @@ -112,6 +219,23 @@ interface MessageServiceAsync { * otherwise the same as [MessageServiceAsync.update]. */ @MustBeClosed + fun update( + messageId: String, + params: MessageUpdateParams, + ): CompletableFuture> = + update(messageId, params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + messageId: String, + params: MessageUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + update(params.toBuilder().messageId(messageId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed fun update(params: MessageUpdateParams): CompletableFuture> = update(params, RequestOptions.none()) @@ -127,10 +251,25 @@ interface MessageServiceAsync { * same as [MessageServiceAsync.list]. */ @MustBeClosed + fun list(threadId: String): CompletableFuture> = + list(threadId, MessageListParams.none()) + + /** @see [list] */ + @MustBeClosed fun list( - params: MessageListParams + threadId: String, + params: MessageListParams = MessageListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> = - list(params, RequestOptions.none()) + list(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed + fun list( + threadId: String, + params: MessageListParams = MessageListParams.none(), + ): CompletableFuture> = + list(threadId, params, RequestOptions.none()) /** @see [list] */ @MustBeClosed @@ -139,11 +278,43 @@ interface MessageServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [list] */ + @MustBeClosed + fun list( + params: MessageListParams + ): CompletableFuture> = + list(params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + threadId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + list(threadId, MessageListParams.none(), requestOptions) + /** * Returns a raw HTTP response for `delete /threads/{thread_id}/messages/{message_id}`, but * is otherwise the same as [MessageServiceAsync.delete]. */ @MustBeClosed + fun delete( + messageId: String, + params: MessageDeleteParams, + ): CompletableFuture> = + delete(messageId, params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + messageId: String, + params: MessageDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + delete(params.toBuilder().messageId(messageId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed fun delete( params: MessageDeleteParams ): 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 9753c5f6..07aad9be 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 @@ -4,6 +4,7 @@ package com.openai.services.async.beta.threads import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -26,6 +27,7 @@ import com.openai.models.beta.threads.messages.MessageListParams import com.openai.models.beta.threads.messages.MessageRetrieveParams import com.openai.models.beta.threads.messages.MessageUpdateParams import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class MessageServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : MessageServiceAsync { @@ -88,6 +90,9 @@ class MessageServiceAsyncImpl internal constructor(private val clientOptions: Cl params: MessageCreateParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -119,6 +124,9 @@ class MessageServiceAsyncImpl internal constructor(private val clientOptions: Cl params: MessageRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("messageId", params.messageId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -154,6 +162,9 @@ class MessageServiceAsyncImpl internal constructor(private val clientOptions: Cl params: MessageUpdateParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("messageId", params.messageId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -191,6 +202,9 @@ class MessageServiceAsyncImpl internal constructor(private val clientOptions: Cl params: MessageListParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -213,6 +227,7 @@ class MessageServiceAsyncImpl internal constructor(private val clientOptions: Cl .let { MessageListPageAsync.builder() .service(MessageServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() @@ -228,6 +243,9 @@ class MessageServiceAsyncImpl internal constructor(private val clientOptions: Cl params: MessageDeleteParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("messageId", params.messageId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) 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 ca8f68ad..660d74de 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 @@ -29,6 +29,18 @@ interface RunServiceAsync { fun steps(): StepServiceAsync /** Create a run. */ + fun create(threadId: String, params: RunCreateParams): CompletableFuture = + create(threadId, params, RequestOptions.none()) + + /** @see [create] */ + fun create( + threadId: String, + params: RunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + create(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [create] */ fun create(params: RunCreateParams): CompletableFuture = create(params, RequestOptions.none()) @@ -39,6 +51,21 @@ interface RunServiceAsync { ): CompletableFuture /** Create a run. */ + fun createStreaming( + threadId: String, + params: RunCreateParams, + ): AsyncStreamResponse = + createStreaming(threadId, params, RequestOptions.none()) + + /** @see [createStreaming] */ + fun createStreaming( + threadId: String, + params: RunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): AsyncStreamResponse = + createStreaming(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [createStreaming] */ fun createStreaming(params: RunCreateParams): AsyncStreamResponse = createStreaming(params, RequestOptions.none()) @@ -49,6 +76,17 @@ interface RunServiceAsync { ): AsyncStreamResponse /** Retrieves a run. */ + fun retrieve(runId: String, params: RunRetrieveParams): CompletableFuture = + retrieve(runId, params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + runId: String, + params: RunRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = retrieve(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [retrieve] */ fun retrieve(params: RunRetrieveParams): CompletableFuture = retrieve(params, RequestOptions.none()) @@ -59,6 +97,17 @@ interface RunServiceAsync { ): CompletableFuture /** Modifies a run. */ + fun update(runId: String, params: RunUpdateParams): CompletableFuture = + update(runId, params, RequestOptions.none()) + + /** @see [update] */ + fun update( + runId: String, + params: RunUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = update(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [update] */ fun update(params: RunUpdateParams): CompletableFuture = update(params, RequestOptions.none()) @@ -69,8 +118,22 @@ interface RunServiceAsync { ): CompletableFuture /** Returns a list of runs belonging to a thread. */ - fun list(params: RunListParams): CompletableFuture = - list(params, RequestOptions.none()) + fun list(threadId: String): CompletableFuture = + list(threadId, RunListParams.none()) + + /** @see [list] */ + fun list( + threadId: String, + params: RunListParams = RunListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + list(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [list] */ + fun list( + threadId: String, + params: RunListParams = RunListParams.none(), + ): CompletableFuture = list(threadId, params, RequestOptions.none()) /** @see [list] */ fun list( @@ -78,7 +141,28 @@ interface RunServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [list] */ + fun list(params: RunListParams): CompletableFuture = + list(params, RequestOptions.none()) + + /** @see [list] */ + fun list( + threadId: String, + requestOptions: RequestOptions, + ): CompletableFuture = list(threadId, RunListParams.none(), requestOptions) + /** Cancels a run that is `in_progress`. */ + fun cancel(runId: String, params: RunCancelParams): CompletableFuture = + cancel(runId, params, RequestOptions.none()) + + /** @see [cancel] */ + fun cancel( + runId: String, + params: RunCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = cancel(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [cancel] */ fun cancel(params: RunCancelParams): CompletableFuture = cancel(params, RequestOptions.none()) @@ -93,6 +177,20 @@ interface RunServiceAsync { * `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. */ + fun submitToolOutputs( + runId: String, + params: RunSubmitToolOutputsParams, + ): CompletableFuture = submitToolOutputs(runId, params, RequestOptions.none()) + + /** @see [submitToolOutputs] */ + fun submitToolOutputs( + runId: String, + params: RunSubmitToolOutputsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + submitToolOutputs(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [submitToolOutputs] */ fun submitToolOutputs(params: RunSubmitToolOutputsParams): CompletableFuture = submitToolOutputs(params, RequestOptions.none()) @@ -107,6 +205,21 @@ interface RunServiceAsync { * `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. */ + fun submitToolOutputsStreaming( + runId: String, + params: RunSubmitToolOutputsParams, + ): AsyncStreamResponse = + submitToolOutputsStreaming(runId, params, RequestOptions.none()) + + /** @see [submitToolOutputsStreaming] */ + fun submitToolOutputsStreaming( + runId: String, + params: RunSubmitToolOutputsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): AsyncStreamResponse = + submitToolOutputsStreaming(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [submitToolOutputsStreaming] */ fun submitToolOutputsStreaming( params: RunSubmitToolOutputsParams ): AsyncStreamResponse = @@ -128,6 +241,22 @@ interface RunServiceAsync { * same as [RunServiceAsync.create]. */ @MustBeClosed + fun create( + threadId: String, + params: RunCreateParams, + ): CompletableFuture> = create(threadId, params, RequestOptions.none()) + + /** @see [create] */ + @MustBeClosed + fun create( + threadId: String, + params: RunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + create(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [create] */ + @MustBeClosed fun create(params: RunCreateParams): CompletableFuture> = create(params, RequestOptions.none()) @@ -143,6 +272,23 @@ interface RunServiceAsync { * same as [RunServiceAsync.createStreaming]. */ @MustBeClosed + fun createStreaming( + threadId: String, + params: RunCreateParams, + ): CompletableFuture>> = + createStreaming(threadId, params, RequestOptions.none()) + + /** @see [createStreaming] */ + @MustBeClosed + fun createStreaming( + threadId: String, + params: RunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> = + createStreaming(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [createStreaming] */ + @MustBeClosed fun createStreaming( params: RunCreateParams ): CompletableFuture>> = @@ -160,6 +306,22 @@ interface RunServiceAsync { * otherwise the same as [RunServiceAsync.retrieve]. */ @MustBeClosed + fun retrieve( + runId: String, + params: RunRetrieveParams, + ): CompletableFuture> = retrieve(runId, params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + runId: String, + params: RunRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve(params: RunRetrieveParams): CompletableFuture> = retrieve(params, RequestOptions.none()) @@ -175,6 +337,22 @@ interface RunServiceAsync { * otherwise the same as [RunServiceAsync.update]. */ @MustBeClosed + fun update( + runId: String, + params: RunUpdateParams, + ): CompletableFuture> = update(runId, params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + runId: String, + params: RunUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + update(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed fun update(params: RunUpdateParams): CompletableFuture> = update(params, RequestOptions.none()) @@ -190,8 +368,25 @@ interface RunServiceAsync { * same as [RunServiceAsync.list]. */ @MustBeClosed - fun list(params: RunListParams): CompletableFuture> = - list(params, RequestOptions.none()) + fun list(threadId: String): CompletableFuture> = + list(threadId, RunListParams.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + threadId: String, + params: RunListParams = RunListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + list(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed + fun list( + threadId: String, + params: RunListParams = RunListParams.none(), + ): CompletableFuture> = + list(threadId, params, RequestOptions.none()) /** @see [list] */ @MustBeClosed @@ -200,11 +395,40 @@ interface RunServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [list] */ + @MustBeClosed + fun list(params: RunListParams): CompletableFuture> = + list(params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + threadId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + list(threadId, RunListParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /threads/{thread_id}/runs/{run_id}/cancel`, but is * otherwise the same as [RunServiceAsync.cancel]. */ @MustBeClosed + fun cancel( + runId: String, + params: RunCancelParams, + ): CompletableFuture> = cancel(runId, params, RequestOptions.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + runId: String, + params: RunCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + cancel(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [cancel] */ + @MustBeClosed fun cancel(params: RunCancelParams): CompletableFuture> = cancel(params, RequestOptions.none()) @@ -221,6 +445,23 @@ interface RunServiceAsync { * [RunServiceAsync.submitToolOutputs]. */ @MustBeClosed + fun submitToolOutputs( + runId: String, + params: RunSubmitToolOutputsParams, + ): CompletableFuture> = + submitToolOutputs(runId, params, RequestOptions.none()) + + /** @see [submitToolOutputs] */ + @MustBeClosed + fun submitToolOutputs( + runId: String, + params: RunSubmitToolOutputsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + submitToolOutputs(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [submitToolOutputs] */ + @MustBeClosed fun submitToolOutputs( params: RunSubmitToolOutputsParams ): CompletableFuture> = @@ -239,6 +480,23 @@ interface RunServiceAsync { * [RunServiceAsync.submitToolOutputsStreaming]. */ @MustBeClosed + fun submitToolOutputsStreaming( + runId: String, + params: RunSubmitToolOutputsParams, + ): CompletableFuture>> = + submitToolOutputsStreaming(runId, params, RequestOptions.none()) + + /** @see [submitToolOutputsStreaming] */ + @MustBeClosed + fun submitToolOutputsStreaming( + runId: String, + params: RunSubmitToolOutputsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> = + submitToolOutputsStreaming(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [submitToolOutputsStreaming] */ + @MustBeClosed fun submitToolOutputsStreaming( params: RunSubmitToolOutputsParams ): 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 9a15dc34..34ff4855 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 @@ -5,6 +5,7 @@ package com.openai.services.async.beta.threads import com.openai.core.ClientOptions import com.openai.core.JsonValue import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.mapJson @@ -36,6 +37,7 @@ import com.openai.models.beta.threads.runs.RunUpdateParams import com.openai.services.async.beta.threads.runs.StepServiceAsync import com.openai.services.async.beta.threads.runs.StepServiceAsyncImpl import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class RunServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : RunServiceAsync { @@ -135,6 +137,9 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client params: RunCreateParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -172,6 +177,9 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client params: RunCreateParams, requestOptions: RequestOptions, ): CompletableFuture>> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -218,6 +226,9 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client params: RunRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -248,6 +259,9 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client params: RunUpdateParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -280,6 +294,9 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client params: RunListParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -302,6 +319,7 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client .let { RunListPageAsync.builder() .service(RunServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() @@ -317,6 +335,9 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client params: RunCancelParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -354,6 +375,9 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client params: RunSubmitToolOutputsParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -394,6 +418,9 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client params: RunSubmitToolOutputsParams, requestOptions: RequestOptions, ): CompletableFuture>> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) 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 3718a766..6f92544a 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 @@ -19,6 +19,18 @@ interface StepServiceAsync { fun withRawResponse(): WithRawResponse /** Retrieves a run step. */ + fun retrieve(stepId: String, params: StepRetrieveParams): CompletableFuture = + retrieve(stepId, params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + stepId: String, + params: StepRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().stepId(stepId).build(), requestOptions) + + /** @see [retrieve] */ fun retrieve(params: StepRetrieveParams): CompletableFuture = retrieve(params, RequestOptions.none()) @@ -29,6 +41,18 @@ interface StepServiceAsync { ): CompletableFuture /** Returns a list of run steps belonging to a run. */ + fun list(runId: String, params: StepListParams): CompletableFuture = + list(runId, params, RequestOptions.none()) + + /** @see [list] */ + fun list( + runId: String, + params: StepListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + list(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [list] */ fun list(params: StepListParams): CompletableFuture = list(params, RequestOptions.none()) @@ -46,6 +70,23 @@ interface StepServiceAsync { * but is otherwise the same as [StepServiceAsync.retrieve]. */ @MustBeClosed + fun retrieve( + stepId: String, + params: StepRetrieveParams, + ): CompletableFuture> = + retrieve(stepId, params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + stepId: String, + params: StepRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().stepId(stepId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve(params: StepRetrieveParams): CompletableFuture> = retrieve(params, RequestOptions.none()) @@ -61,6 +102,23 @@ interface StepServiceAsync { * otherwise the same as [StepServiceAsync.list]. */ @MustBeClosed + fun list( + runId: String, + params: StepListParams, + ): CompletableFuture> = + list(runId, params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + runId: String, + params: StepListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + list(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed fun list(params: StepListParams): CompletableFuture> = list(params, RequestOptions.none()) 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 9f3f8350..49b9797b 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 @@ -4,6 +4,7 @@ package com.openai.services.async.beta.threads.runs import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -21,6 +22,7 @@ import com.openai.models.beta.threads.runs.steps.StepListPageResponse import com.openai.models.beta.threads.runs.steps.StepListParams import com.openai.models.beta.threads.runs.steps.StepRetrieveParams import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class StepServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : StepServiceAsync { @@ -62,6 +64,9 @@ class StepServiceAsyncImpl internal constructor(private val clientOptions: Clien params: StepRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("stepId", params.stepId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -100,6 +105,9 @@ class StepServiceAsyncImpl internal constructor(private val clientOptions: Clien params: StepListParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -128,6 +136,7 @@ class StepServiceAsyncImpl internal constructor(private val clientOptions: Clien .let { StepListPageAsync.builder() .service(StepServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/chat/ChatCompletionServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/chat/ChatCompletionServiceAsync.kt index 50e7adff..2890cb31 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/chat/ChatCompletionServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/chat/ChatCompletionServiceAsync.kt @@ -87,8 +87,22 @@ interface ChatCompletionServiceAsync { * Get a stored chat completion. Only Chat Completions that have been created with the `store` * parameter set to `true` will be returned. */ - fun retrieve(params: ChatCompletionRetrieveParams): CompletableFuture = - retrieve(params, RequestOptions.none()) + fun retrieve(completionId: String): CompletableFuture = + retrieve(completionId, ChatCompletionRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + completionId: String, + params: ChatCompletionRetrieveParams = ChatCompletionRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + completionId: String, + params: ChatCompletionRetrieveParams = ChatCompletionRetrieveParams.none(), + ): CompletableFuture = retrieve(completionId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -96,11 +110,36 @@ interface ChatCompletionServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [retrieve] */ + fun retrieve(params: ChatCompletionRetrieveParams): CompletableFuture = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + completionId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + retrieve(completionId, ChatCompletionRetrieveParams.none(), requestOptions) + /** * 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. */ + fun update( + completionId: String, + params: ChatCompletionUpdateParams, + ): CompletableFuture = update(completionId, params, RequestOptions.none()) + + /** @see [update] */ + fun update( + completionId: String, + params: ChatCompletionUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + update(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [update] */ fun update(params: ChatCompletionUpdateParams): CompletableFuture = update(params, RequestOptions.none()) @@ -136,8 +175,23 @@ interface ChatCompletionServiceAsync { * Delete a stored chat completion. Only Chat Completions that have been created with the * `store` parameter set to `true` can be deleted. */ - fun delete(params: ChatCompletionDeleteParams): CompletableFuture = - delete(params, RequestOptions.none()) + fun delete(completionId: String): CompletableFuture = + delete(completionId, ChatCompletionDeleteParams.none()) + + /** @see [delete] */ + fun delete( + completionId: String, + params: ChatCompletionDeleteParams = ChatCompletionDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [delete] */ + fun delete( + completionId: String, + params: ChatCompletionDeleteParams = ChatCompletionDeleteParams.none(), + ): CompletableFuture = + delete(completionId, params, RequestOptions.none()) /** @see [delete] */ fun delete( @@ -145,6 +199,17 @@ interface ChatCompletionServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [delete] */ + fun delete(params: ChatCompletionDeleteParams): CompletableFuture = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete( + completionId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + delete(completionId, ChatCompletionDeleteParams.none(), requestOptions) + /** * A view of [ChatCompletionServiceAsync] that provides access to raw HTTP responses for each * method. @@ -192,10 +257,25 @@ interface ChatCompletionServiceAsync { * the same as [ChatCompletionServiceAsync.retrieve]. */ @MustBeClosed + fun retrieve(completionId: String): CompletableFuture> = + retrieve(completionId, ChatCompletionRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve( - params: ChatCompletionRetrieveParams + completionId: String, + params: ChatCompletionRetrieveParams = ChatCompletionRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> = - retrieve(params, RequestOptions.none()) + retrieve(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + completionId: String, + params: ChatCompletionRetrieveParams = ChatCompletionRetrieveParams.none(), + ): CompletableFuture> = + retrieve(completionId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -204,11 +284,43 @@ interface ChatCompletionServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + params: ChatCompletionRetrieveParams + ): CompletableFuture> = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + completionId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + retrieve(completionId, ChatCompletionRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /chat/completions/{completion_id}`, but is * otherwise the same as [ChatCompletionServiceAsync.update]. */ @MustBeClosed + fun update( + completionId: String, + params: ChatCompletionUpdateParams, + ): CompletableFuture> = + update(completionId, params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + completionId: String, + params: ChatCompletionUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + update(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed fun update( params: ChatCompletionUpdateParams ): CompletableFuture> = @@ -256,9 +368,26 @@ interface ChatCompletionServiceAsync { */ @MustBeClosed fun delete( - params: ChatCompletionDeleteParams + completionId: String ): CompletableFuture> = - delete(params, RequestOptions.none()) + delete(completionId, ChatCompletionDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + completionId: String, + params: ChatCompletionDeleteParams = ChatCompletionDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + delete(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + completionId: String, + params: ChatCompletionDeleteParams = ChatCompletionDeleteParams.none(), + ): CompletableFuture> = + delete(completionId, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -266,5 +395,20 @@ interface ChatCompletionServiceAsync { params: ChatCompletionDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + + /** @see [delete] */ + @MustBeClosed + fun delete( + params: ChatCompletionDeleteParams + ): CompletableFuture> = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + completionId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + delete(completionId, ChatCompletionDeleteParams.none(), requestOptions) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/chat/ChatCompletionServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/chat/ChatCompletionServiceAsyncImpl.kt index 783a608d..260a4619 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/chat/ChatCompletionServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/chat/ChatCompletionServiceAsyncImpl.kt @@ -5,6 +5,7 @@ package com.openai.services.async.chat import com.openai.core.ClientOptions import com.openai.core.JsonValue import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.mapJson @@ -186,6 +187,9 @@ internal constructor(private val clientOptions: ClientOptions) : ChatCompletionS params: ChatCompletionRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("completionId", params.completionId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -215,6 +219,9 @@ internal constructor(private val clientOptions: ClientOptions) : ChatCompletionS params: ChatCompletionUpdateParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("completionId", params.completionId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -271,6 +278,7 @@ internal constructor(private val clientOptions: ClientOptions) : ChatCompletionS .let { ChatCompletionListPageAsync.builder() .service(ChatCompletionServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() @@ -287,6 +295,9 @@ internal constructor(private val clientOptions: ClientOptions) : ChatCompletionS params: ChatCompletionDeleteParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("completionId", params.completionId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) 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 3c5d17d3..6edcc5a0 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 @@ -20,8 +20,22 @@ interface MessageServiceAsync { * 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. */ - fun list(params: MessageListParams): CompletableFuture = - list(params, RequestOptions.none()) + fun list(completionId: String): CompletableFuture = + list(completionId, MessageListParams.none()) + + /** @see [list] */ + fun list( + completionId: String, + params: MessageListParams = MessageListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + list(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [list] */ + fun list( + completionId: String, + params: MessageListParams = MessageListParams.none(), + ): CompletableFuture = list(completionId, params, RequestOptions.none()) /** @see [list] */ fun list( @@ -29,6 +43,17 @@ interface MessageServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [list] */ + fun list(params: MessageListParams): CompletableFuture = + list(params, RequestOptions.none()) + + /** @see [list] */ + fun list( + completionId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + list(completionId, MessageListParams.none(), requestOptions) + /** * A view of [MessageServiceAsync] that provides access to raw HTTP responses for each method. */ @@ -39,10 +64,25 @@ interface MessageServiceAsync { * otherwise the same as [MessageServiceAsync.list]. */ @MustBeClosed + fun list(completionId: String): CompletableFuture> = + list(completionId, MessageListParams.none()) + + /** @see [list] */ + @MustBeClosed fun list( - params: MessageListParams + completionId: String, + params: MessageListParams = MessageListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> = - list(params, RequestOptions.none()) + list(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed + fun list( + completionId: String, + params: MessageListParams = MessageListParams.none(), + ): CompletableFuture> = + list(completionId, params, RequestOptions.none()) /** @see [list] */ @MustBeClosed @@ -50,5 +90,20 @@ interface MessageServiceAsync { params: MessageListParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + + /** @see [list] */ + @MustBeClosed + fun list( + params: MessageListParams + ): CompletableFuture> = + list(params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + completionId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + list(completionId, MessageListParams.none(), requestOptions) } } 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 c66410b5..271a18a6 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 @@ -4,6 +4,7 @@ package com.openai.services.async.chat.completions import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -18,6 +19,7 @@ import com.openai.models.chat.completions.messages.MessageListPageAsync import com.openai.models.chat.completions.messages.MessageListPageResponse import com.openai.models.chat.completions.messages.MessageListParams import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class MessageServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : MessageServiceAsync { @@ -48,6 +50,9 @@ class MessageServiceAsyncImpl internal constructor(private val clientOptions: Cl params: MessageListParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("completionId", params.completionId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -69,6 +74,7 @@ class MessageServiceAsyncImpl internal constructor(private val clientOptions: Cl .let { MessageListPageAsync.builder() .service(MessageServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/evals/RunServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/evals/RunServiceAsync.kt index 801e00e4..55f66db0 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/evals/RunServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/evals/RunServiceAsync.kt @@ -27,7 +27,23 @@ interface RunServiceAsync { fun outputItems(): OutputItemServiceAsync - /** Create a new evaluation run. This is the endpoint that will kick off grading. */ + /** + * Kicks off a new run for a given evaluation, specifying the data source, and what model + * configuration to use to test. The datasource will be validated against the schema specified + * in the config of the evaluation. + */ + fun create(evalId: String, params: RunCreateParams): CompletableFuture = + create(evalId, params, RequestOptions.none()) + + /** @see [create] */ + fun create( + evalId: String, + params: RunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + create(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [create] */ fun create(params: RunCreateParams): CompletableFuture = create(params, RequestOptions.none()) @@ -38,6 +54,18 @@ interface RunServiceAsync { ): CompletableFuture /** Get an evaluation run by ID. */ + fun retrieve(runId: String, params: RunRetrieveParams): CompletableFuture = + retrieve(runId, params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + runId: String, + params: RunRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [retrieve] */ fun retrieve(params: RunRetrieveParams): CompletableFuture = retrieve(params, RequestOptions.none()) @@ -48,8 +76,22 @@ interface RunServiceAsync { ): CompletableFuture /** Get a list of runs for an evaluation. */ - fun list(params: RunListParams): CompletableFuture = - list(params, RequestOptions.none()) + fun list(evalId: String): CompletableFuture = + list(evalId, RunListParams.none()) + + /** @see [list] */ + fun list( + evalId: String, + params: RunListParams = RunListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + list(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [list] */ + fun list( + evalId: String, + params: RunListParams = RunListParams.none(), + ): CompletableFuture = list(evalId, params, RequestOptions.none()) /** @see [list] */ fun list( @@ -57,7 +99,27 @@ interface RunServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [list] */ + fun list(params: RunListParams): CompletableFuture = + list(params, RequestOptions.none()) + + /** @see [list] */ + fun list(evalId: String, requestOptions: RequestOptions): CompletableFuture = + list(evalId, RunListParams.none(), requestOptions) + /** Delete an eval run. */ + fun delete(runId: String, params: RunDeleteParams): CompletableFuture = + delete(runId, params, RequestOptions.none()) + + /** @see [delete] */ + fun delete( + runId: String, + params: RunDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [delete] */ fun delete(params: RunDeleteParams): CompletableFuture = delete(params, RequestOptions.none()) @@ -68,6 +130,18 @@ interface RunServiceAsync { ): CompletableFuture /** Cancel an ongoing evaluation run. */ + fun cancel(runId: String, params: RunCancelParams): CompletableFuture = + cancel(runId, params, RequestOptions.none()) + + /** @see [cancel] */ + fun cancel( + runId: String, + params: RunCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + cancel(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [cancel] */ fun cancel(params: RunCancelParams): CompletableFuture = cancel(params, RequestOptions.none()) @@ -87,6 +161,23 @@ interface RunServiceAsync { * as [RunServiceAsync.create]. */ @MustBeClosed + fun create( + evalId: String, + params: RunCreateParams, + ): CompletableFuture> = + create(evalId, params, RequestOptions.none()) + + /** @see [create] */ + @MustBeClosed + fun create( + evalId: String, + params: RunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + create(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [create] */ + @MustBeClosed fun create(params: RunCreateParams): CompletableFuture> = create(params, RequestOptions.none()) @@ -102,6 +193,23 @@ interface RunServiceAsync { * the same as [RunServiceAsync.retrieve]. */ @MustBeClosed + fun retrieve( + runId: String, + params: RunRetrieveParams, + ): CompletableFuture> = + retrieve(runId, params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + runId: String, + params: RunRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve( params: RunRetrieveParams ): CompletableFuture> = @@ -119,8 +227,25 @@ interface RunServiceAsync { * [RunServiceAsync.list]. */ @MustBeClosed - fun list(params: RunListParams): CompletableFuture> = - list(params, RequestOptions.none()) + fun list(evalId: String): CompletableFuture> = + list(evalId, RunListParams.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + evalId: String, + params: RunListParams = RunListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + list(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed + fun list( + evalId: String, + params: RunListParams = RunListParams.none(), + ): CompletableFuture> = + list(evalId, params, RequestOptions.none()) /** @see [list] */ @MustBeClosed @@ -129,11 +254,41 @@ interface RunServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [list] */ + @MustBeClosed + fun list(params: RunListParams): CompletableFuture> = + list(params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + evalId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + list(evalId, RunListParams.none(), requestOptions) + /** * Returns a raw HTTP response for `delete /evals/{eval_id}/runs/{run_id}`, but is otherwise * the same as [RunServiceAsync.delete]. */ @MustBeClosed + fun delete( + runId: String, + params: RunDeleteParams, + ): CompletableFuture> = + delete(runId, params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + runId: String, + params: RunDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + delete(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed fun delete(params: RunDeleteParams): CompletableFuture> = delete(params, RequestOptions.none()) @@ -149,6 +304,23 @@ interface RunServiceAsync { * the same as [RunServiceAsync.cancel]. */ @MustBeClosed + fun cancel( + runId: String, + params: RunCancelParams, + ): CompletableFuture> = + cancel(runId, params, RequestOptions.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + runId: String, + params: RunCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + cancel(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [cancel] */ + @MustBeClosed fun cancel(params: RunCancelParams): CompletableFuture> = cancel(params, RequestOptions.none()) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/evals/RunServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/evals/RunServiceAsyncImpl.kt index b39590a1..b5d6eb0e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/evals/RunServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/evals/RunServiceAsyncImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.async.evals import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -29,6 +30,7 @@ import com.openai.models.evals.runs.RunRetrieveResponse import com.openai.services.async.evals.runs.OutputItemServiceAsync import com.openai.services.async.evals.runs.OutputItemServiceAsyncImpl import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class RunServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : RunServiceAsync { @@ -98,6 +100,9 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client params: RunCreateParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("evalId", params.evalId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -129,6 +134,9 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client params: RunRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -159,6 +167,9 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client params: RunListParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("evalId", params.evalId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -180,6 +191,7 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client .let { RunListPageAsync.builder() .service(RunServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() @@ -195,6 +207,9 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client params: RunDeleteParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) @@ -225,6 +240,9 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client params: RunCancelParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/evals/runs/OutputItemServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/evals/runs/OutputItemServiceAsync.kt index 705932c0..6373f511 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/evals/runs/OutputItemServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/evals/runs/OutputItemServiceAsync.kt @@ -19,6 +19,21 @@ interface OutputItemServiceAsync { fun withRawResponse(): WithRawResponse /** Get an evaluation run output item by ID. */ + fun retrieve( + outputItemId: String, + params: OutputItemRetrieveParams, + ): CompletableFuture = + retrieve(outputItemId, params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + outputItemId: String, + params: OutputItemRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().outputItemId(outputItemId).build(), requestOptions) + + /** @see [retrieve] */ fun retrieve(params: OutputItemRetrieveParams): CompletableFuture = retrieve(params, RequestOptions.none()) @@ -29,6 +44,20 @@ interface OutputItemServiceAsync { ): CompletableFuture /** Get a list of output items for an evaluation run. */ + fun list( + runId: String, + params: OutputItemListParams, + ): CompletableFuture = list(runId, params, RequestOptions.none()) + + /** @see [list] */ + fun list( + runId: String, + params: OutputItemListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + list(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [list] */ fun list(params: OutputItemListParams): CompletableFuture = list(params, RequestOptions.none()) @@ -50,6 +79,23 @@ interface OutputItemServiceAsync { * as [OutputItemServiceAsync.retrieve]. */ @MustBeClosed + fun retrieve( + outputItemId: String, + params: OutputItemRetrieveParams, + ): CompletableFuture> = + retrieve(outputItemId, params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + outputItemId: String, + params: OutputItemRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().outputItemId(outputItemId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve( params: OutputItemRetrieveParams ): CompletableFuture> = @@ -67,6 +113,23 @@ interface OutputItemServiceAsync { * otherwise the same as [OutputItemServiceAsync.list]. */ @MustBeClosed + fun list( + runId: String, + params: OutputItemListParams, + ): CompletableFuture> = + list(runId, params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + runId: String, + params: OutputItemListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + list(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed fun list( params: OutputItemListParams ): CompletableFuture> = diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/evals/runs/OutputItemServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/evals/runs/OutputItemServiceAsyncImpl.kt index 05537bed..ad4cd7fa 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/evals/runs/OutputItemServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/evals/runs/OutputItemServiceAsyncImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.async.evals.runs import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -20,6 +21,7 @@ import com.openai.models.evals.runs.outputitems.OutputItemListParams import com.openai.models.evals.runs.outputitems.OutputItemRetrieveParams import com.openai.models.evals.runs.outputitems.OutputItemRetrieveResponse import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class OutputItemServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : OutputItemServiceAsync { @@ -57,6 +59,9 @@ class OutputItemServiceAsyncImpl internal constructor(private val clientOptions: params: OutputItemRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("outputItemId", params.outputItemId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -94,6 +99,9 @@ class OutputItemServiceAsyncImpl internal constructor(private val clientOptions: params: OutputItemListParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -121,6 +129,7 @@ class OutputItemServiceAsyncImpl internal constructor(private val clientOptions: .let { OutputItemListPageAsync.builder() .service(OutputItemServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/AlphaServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/AlphaServiceAsync.kt new file mode 100644 index 00000000..58bc3d54 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/AlphaServiceAsync.kt @@ -0,0 +1,21 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.finetuning + +import com.openai.services.async.finetuning.alpha.GraderServiceAsync + +interface AlphaServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + fun graders(): GraderServiceAsync + + /** A view of [AlphaServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun graders(): GraderServiceAsync.WithRawResponse + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/AlphaServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/AlphaServiceAsyncImpl.kt new file mode 100644 index 00000000..fff90acb --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/AlphaServiceAsyncImpl.kt @@ -0,0 +1,31 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.finetuning + +import com.openai.core.ClientOptions +import com.openai.services.async.finetuning.alpha.GraderServiceAsync +import com.openai.services.async.finetuning.alpha.GraderServiceAsyncImpl + +class AlphaServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + AlphaServiceAsync { + + private val withRawResponse: AlphaServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val graders: GraderServiceAsync by lazy { GraderServiceAsyncImpl(clientOptions) } + + override fun withRawResponse(): AlphaServiceAsync.WithRawResponse = withRawResponse + + override fun graders(): GraderServiceAsync = graders + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + AlphaServiceAsync.WithRawResponse { + + private val graders: GraderServiceAsync.WithRawResponse by lazy { + GraderServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun graders(): GraderServiceAsync.WithRawResponse = graders + } +} 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 b19633ee..9ed46708 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 @@ -12,6 +12,8 @@ import com.openai.models.finetuning.jobs.JobListEventsPageAsync import com.openai.models.finetuning.jobs.JobListEventsParams import com.openai.models.finetuning.jobs.JobListPageAsync import com.openai.models.finetuning.jobs.JobListParams +import com.openai.models.finetuning.jobs.JobPauseParams +import com.openai.models.finetuning.jobs.JobResumeParams import com.openai.models.finetuning.jobs.JobRetrieveParams import com.openai.services.async.finetuning.jobs.CheckpointServiceAsync import java.util.concurrent.CompletableFuture @@ -48,8 +50,22 @@ interface JobServiceAsync { * * [Learn more about fine-tuning](https://platform.openai.com/docs/guides/fine-tuning) */ - fun retrieve(params: JobRetrieveParams): CompletableFuture = - retrieve(params, RequestOptions.none()) + fun retrieve(fineTuningJobId: String): CompletableFuture = + retrieve(fineTuningJobId, JobRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + fineTuningJobId: String, + params: JobRetrieveParams = JobRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + fineTuningJobId: String, + params: JobRetrieveParams = JobRetrieveParams.none(), + ): CompletableFuture = retrieve(fineTuningJobId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -57,6 +73,17 @@ interface JobServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [retrieve] */ + fun retrieve(params: JobRetrieveParams): CompletableFuture = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + retrieve(fineTuningJobId, JobRetrieveParams.none(), requestOptions) + /** List your organization's fine-tuning jobs */ fun list(): CompletableFuture = list(JobListParams.none()) @@ -75,8 +102,22 @@ interface JobServiceAsync { list(JobListParams.none(), requestOptions) /** Immediately cancel a fine-tune job. */ - fun cancel(params: JobCancelParams): CompletableFuture = - cancel(params, RequestOptions.none()) + fun cancel(fineTuningJobId: String): CompletableFuture = + cancel(fineTuningJobId, JobCancelParams.none()) + + /** @see [cancel] */ + fun cancel( + fineTuningJobId: String, + params: JobCancelParams = JobCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + cancel(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [cancel] */ + fun cancel( + fineTuningJobId: String, + params: JobCancelParams = JobCancelParams.none(), + ): CompletableFuture = cancel(fineTuningJobId, params, RequestOptions.none()) /** @see [cancel] */ fun cancel( @@ -84,9 +125,35 @@ interface JobServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [cancel] */ + fun cancel(params: JobCancelParams): CompletableFuture = + cancel(params, RequestOptions.none()) + + /** @see [cancel] */ + fun cancel( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + cancel(fineTuningJobId, JobCancelParams.none(), requestOptions) + /** Get status updates for a fine-tuning job. */ - fun listEvents(params: JobListEventsParams): CompletableFuture = - listEvents(params, RequestOptions.none()) + fun listEvents(fineTuningJobId: String): CompletableFuture = + listEvents(fineTuningJobId, JobListEventsParams.none()) + + /** @see [listEvents] */ + fun listEvents( + fineTuningJobId: String, + params: JobListEventsParams = JobListEventsParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + listEvents(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [listEvents] */ + fun listEvents( + fineTuningJobId: String, + params: JobListEventsParams = JobListEventsParams.none(), + ): CompletableFuture = + listEvents(fineTuningJobId, params, RequestOptions.none()) /** @see [listEvents] */ fun listEvents( @@ -94,6 +161,87 @@ interface JobServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [listEvents] */ + fun listEvents(params: JobListEventsParams): CompletableFuture = + listEvents(params, RequestOptions.none()) + + /** @see [listEvents] */ + fun listEvents( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + listEvents(fineTuningJobId, JobListEventsParams.none(), requestOptions) + + /** Pause a fine-tune job. */ + fun pause(fineTuningJobId: String): CompletableFuture = + pause(fineTuningJobId, JobPauseParams.none()) + + /** @see [pause] */ + fun pause( + fineTuningJobId: String, + params: JobPauseParams = JobPauseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + pause(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [pause] */ + fun pause( + fineTuningJobId: String, + params: JobPauseParams = JobPauseParams.none(), + ): CompletableFuture = pause(fineTuningJobId, params, RequestOptions.none()) + + /** @see [pause] */ + fun pause( + params: JobPauseParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see [pause] */ + fun pause(params: JobPauseParams): CompletableFuture = + pause(params, RequestOptions.none()) + + /** @see [pause] */ + fun pause( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + pause(fineTuningJobId, JobPauseParams.none(), requestOptions) + + /** Resume a fine-tune job. */ + fun resume(fineTuningJobId: String): CompletableFuture = + resume(fineTuningJobId, JobResumeParams.none()) + + /** @see [resume] */ + fun resume( + fineTuningJobId: String, + params: JobResumeParams = JobResumeParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + resume(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [resume] */ + fun resume( + fineTuningJobId: String, + params: JobResumeParams = JobResumeParams.none(), + ): CompletableFuture = resume(fineTuningJobId, params, RequestOptions.none()) + + /** @see [resume] */ + fun resume( + params: JobResumeParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see [resume] */ + fun resume(params: JobResumeParams): CompletableFuture = + resume(params, RequestOptions.none()) + + /** @see [resume] */ + fun resume( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + resume(fineTuningJobId, JobResumeParams.none(), requestOptions) + /** A view of [JobServiceAsync] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -119,8 +267,25 @@ interface JobServiceAsync { * otherwise the same as [JobServiceAsync.retrieve]. */ @MustBeClosed - fun retrieve(params: JobRetrieveParams): CompletableFuture> = - retrieve(params, RequestOptions.none()) + fun retrieve(fineTuningJobId: String): CompletableFuture> = + retrieve(fineTuningJobId, JobRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fineTuningJobId: String, + params: JobRetrieveParams = JobRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fineTuningJobId: String, + params: JobRetrieveParams = JobRetrieveParams.none(), + ): CompletableFuture> = + retrieve(fineTuningJobId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -129,6 +294,19 @@ interface JobServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: JobRetrieveParams): CompletableFuture> = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + retrieve(fineTuningJobId, JobRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /fine_tuning/jobs`, but is otherwise the same as * [JobServiceAsync.list]. @@ -163,8 +341,25 @@ interface JobServiceAsync { * is otherwise the same as [JobServiceAsync.cancel]. */ @MustBeClosed - fun cancel(params: JobCancelParams): CompletableFuture> = - cancel(params, RequestOptions.none()) + fun cancel(fineTuningJobId: String): CompletableFuture> = + cancel(fineTuningJobId, JobCancelParams.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + fineTuningJobId: String, + params: JobCancelParams = JobCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + cancel(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + fineTuningJobId: String, + params: JobCancelParams = JobCancelParams.none(), + ): CompletableFuture> = + cancel(fineTuningJobId, params, RequestOptions.none()) /** @see [cancel] */ @MustBeClosed @@ -173,15 +368,45 @@ interface JobServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [cancel] */ + @MustBeClosed + fun cancel(params: JobCancelParams): CompletableFuture> = + cancel(params, RequestOptions.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + cancel(fineTuningJobId, JobCancelParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /fine_tuning/jobs/{fine_tuning_job_id}/events`, but * is otherwise the same as [JobServiceAsync.listEvents]. */ @MustBeClosed fun listEvents( - params: JobListEventsParams + fineTuningJobId: String ): CompletableFuture> = - listEvents(params, RequestOptions.none()) + listEvents(fineTuningJobId, JobListEventsParams.none()) + + /** @see [listEvents] */ + @MustBeClosed + fun listEvents( + fineTuningJobId: String, + params: JobListEventsParams = JobListEventsParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + listEvents(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [listEvents] */ + @MustBeClosed + fun listEvents( + fineTuningJobId: String, + params: JobListEventsParams = JobListEventsParams.none(), + ): CompletableFuture> = + listEvents(fineTuningJobId, params, RequestOptions.none()) /** @see [listEvents] */ @MustBeClosed @@ -189,5 +414,110 @@ interface JobServiceAsync { params: JobListEventsParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + + /** @see [listEvents] */ + @MustBeClosed + fun listEvents( + params: JobListEventsParams + ): CompletableFuture> = + listEvents(params, RequestOptions.none()) + + /** @see [listEvents] */ + @MustBeClosed + fun listEvents( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + listEvents(fineTuningJobId, JobListEventsParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /fine_tuning/jobs/{fine_tuning_job_id}/pause`, but + * is otherwise the same as [JobServiceAsync.pause]. + */ + @MustBeClosed + fun pause(fineTuningJobId: String): CompletableFuture> = + pause(fineTuningJobId, JobPauseParams.none()) + + /** @see [pause] */ + @MustBeClosed + fun pause( + fineTuningJobId: String, + params: JobPauseParams = JobPauseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + pause(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [pause] */ + @MustBeClosed + fun pause( + fineTuningJobId: String, + params: JobPauseParams = JobPauseParams.none(), + ): CompletableFuture> = + pause(fineTuningJobId, params, RequestOptions.none()) + + /** @see [pause] */ + @MustBeClosed + fun pause( + params: JobPauseParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see [pause] */ + @MustBeClosed + fun pause(params: JobPauseParams): CompletableFuture> = + pause(params, RequestOptions.none()) + + /** @see [pause] */ + @MustBeClosed + fun pause( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + pause(fineTuningJobId, JobPauseParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /fine_tuning/jobs/{fine_tuning_job_id}/resume`, but + * is otherwise the same as [JobServiceAsync.resume]. + */ + @MustBeClosed + fun resume(fineTuningJobId: String): CompletableFuture> = + resume(fineTuningJobId, JobResumeParams.none()) + + /** @see [resume] */ + @MustBeClosed + fun resume( + fineTuningJobId: String, + params: JobResumeParams = JobResumeParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + resume(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [resume] */ + @MustBeClosed + fun resume( + fineTuningJobId: String, + params: JobResumeParams = JobResumeParams.none(), + ): CompletableFuture> = + resume(fineTuningJobId, params, RequestOptions.none()) + + /** @see [resume] */ + @MustBeClosed + fun resume( + params: JobResumeParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see [resume] */ + @MustBeClosed + fun resume(params: JobResumeParams): CompletableFuture> = + resume(params, RequestOptions.none()) + + /** @see [resume] */ + @MustBeClosed + fun resume( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + resume(fineTuningJobId, JobResumeParams.none(), requestOptions) } } 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 5ebe2d42..959d5660 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 @@ -4,6 +4,7 @@ package com.openai.services.async.finetuning import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -24,10 +25,13 @@ import com.openai.models.finetuning.jobs.JobListEventsParams import com.openai.models.finetuning.jobs.JobListPageAsync import com.openai.models.finetuning.jobs.JobListPageResponse import com.openai.models.finetuning.jobs.JobListParams +import com.openai.models.finetuning.jobs.JobPauseParams +import com.openai.models.finetuning.jobs.JobResumeParams import com.openai.models.finetuning.jobs.JobRetrieveParams import com.openai.services.async.finetuning.jobs.CheckpointServiceAsync import com.openai.services.async.finetuning.jobs.CheckpointServiceAsyncImpl import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class JobServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : JobServiceAsync { @@ -79,6 +83,20 @@ class JobServiceAsyncImpl internal constructor(private val clientOptions: Client // get /fine_tuning/jobs/{fine_tuning_job_id}/events withRawResponse().listEvents(params, requestOptions).thenApply { it.parse() } + override fun pause( + params: JobPauseParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /fine_tuning/jobs/{fine_tuning_job_id}/pause + withRawResponse().pause(params, requestOptions).thenApply { it.parse() } + + override fun resume( + params: JobResumeParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /fine_tuning/jobs/{fine_tuning_job_id}/resume + withRawResponse().resume(params, requestOptions).thenApply { it.parse() } + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : JobServiceAsync.WithRawResponse { @@ -127,6 +145,9 @@ class JobServiceAsyncImpl internal constructor(private val clientOptions: Client params: JobRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTuningJobId", params.fineTuningJobId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -178,6 +199,7 @@ class JobServiceAsyncImpl internal constructor(private val clientOptions: Client .let { JobListPageAsync.builder() .service(JobServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() @@ -193,6 +215,9 @@ class JobServiceAsyncImpl internal constructor(private val clientOptions: Client params: JobCancelParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTuningJobId", params.fineTuningJobId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -224,6 +249,9 @@ class JobServiceAsyncImpl internal constructor(private val clientOptions: Client params: JobListEventsParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTuningJobId", params.fineTuningJobId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -245,6 +273,7 @@ class JobServiceAsyncImpl internal constructor(private val clientOptions: Client .let { JobListEventsPageAsync.builder() .service(JobServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() @@ -252,5 +281,71 @@ class JobServiceAsyncImpl internal constructor(private val clientOptions: Client } } } + + private val pauseHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun pause( + params: JobPauseParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTuningJobId", params.fineTuningJobId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("fine_tuning", "jobs", params._pathParam(0), "pause") + .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 { pauseHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val resumeHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun resume( + params: JobResumeParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTuningJobId", params.fineTuningJobId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("fine_tuning", "jobs", params._pathParam(0), "resume") + .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 { resumeHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/MethodServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/MethodServiceAsync.kt new file mode 100644 index 00000000..6c46a35d --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/MethodServiceAsync.kt @@ -0,0 +1,16 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.finetuning + +interface MethodServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * A view of [MethodServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/MethodServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/MethodServiceAsyncImpl.kt new file mode 100644 index 00000000..a2803029 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/MethodServiceAsyncImpl.kt @@ -0,0 +1,18 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.finetuning + +import com.openai.core.ClientOptions + +class MethodServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + MethodServiceAsync { + + private val withRawResponse: MethodServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): MethodServiceAsync.WithRawResponse = withRawResponse + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + MethodServiceAsync.WithRawResponse +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/alpha/GraderServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/alpha/GraderServiceAsync.kt new file mode 100644 index 00000000..a11d77f1 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/alpha/GraderServiceAsync.kt @@ -0,0 +1,78 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.finetuning.alpha + +import com.google.errorprone.annotations.MustBeClosed +import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor +import com.openai.models.finetuning.alpha.graders.GraderRunParams +import com.openai.models.finetuning.alpha.graders.GraderRunResponse +import com.openai.models.finetuning.alpha.graders.GraderValidateParams +import com.openai.models.finetuning.alpha.graders.GraderValidateResponse +import java.util.concurrent.CompletableFuture + +interface GraderServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** Run a grader. */ + fun run(params: GraderRunParams): CompletableFuture = + run(params, RequestOptions.none()) + + /** @see [run] */ + fun run( + params: GraderRunParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** Validate a grader. */ + fun validate(params: GraderValidateParams): CompletableFuture = + validate(params, RequestOptions.none()) + + /** @see [validate] */ + fun validate( + params: GraderValidateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** + * A view of [GraderServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /fine_tuning/alpha/graders/run`, but is otherwise + * the same as [GraderServiceAsync.run]. + */ + @MustBeClosed + fun run(params: GraderRunParams): CompletableFuture> = + run(params, RequestOptions.none()) + + /** @see [run] */ + @MustBeClosed + fun run( + params: GraderRunParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /fine_tuning/alpha/graders/validate`, but is + * otherwise the same as [GraderServiceAsync.validate]. + */ + @MustBeClosed + fun validate( + params: GraderValidateParams + ): CompletableFuture> = + validate(params, RequestOptions.none()) + + /** @see [validate] */ + @MustBeClosed + fun validate( + params: GraderValidateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/alpha/GraderServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/alpha/GraderServiceAsyncImpl.kt new file mode 100644 index 00000000..9eb20ee4 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/alpha/GraderServiceAsyncImpl.kt @@ -0,0 +1,113 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.finetuning.alpha + +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.json +import com.openai.core.http.parseable +import com.openai.core.prepareAsync +import com.openai.models.ErrorObject +import com.openai.models.finetuning.alpha.graders.GraderRunParams +import com.openai.models.finetuning.alpha.graders.GraderRunResponse +import com.openai.models.finetuning.alpha.graders.GraderValidateParams +import com.openai.models.finetuning.alpha.graders.GraderValidateResponse +import java.util.concurrent.CompletableFuture + +class GraderServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + GraderServiceAsync { + + private val withRawResponse: GraderServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): GraderServiceAsync.WithRawResponse = withRawResponse + + override fun run( + params: GraderRunParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /fine_tuning/alpha/graders/run + withRawResponse().run(params, requestOptions).thenApply { it.parse() } + + override fun validate( + params: GraderValidateParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /fine_tuning/alpha/graders/validate + withRawResponse().validate(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + GraderServiceAsync.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val runHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun run( + params: GraderRunParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("fine_tuning", "alpha", "graders", "run") + .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 { runHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val validateHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun validate( + params: GraderValidateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("fine_tuning", "alpha", "graders", "validate") + .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 { validateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/checkpoints/PermissionServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/checkpoints/PermissionServiceAsync.kt index 6fa0d154..0d293bcb 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/checkpoints/PermissionServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/checkpoints/PermissionServiceAsync.kt @@ -26,6 +26,24 @@ interface PermissionServiceAsync { * This enables organization owners to share fine-tuned models with other projects in their * organization. */ + fun create( + fineTunedModelCheckpoint: String, + params: PermissionCreateParams, + ): CompletableFuture = + create(fineTunedModelCheckpoint, params, RequestOptions.none()) + + /** @see [create] */ + fun create( + fineTunedModelCheckpoint: String, + params: PermissionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + create( + params.toBuilder().fineTunedModelCheckpoint(fineTunedModelCheckpoint).build(), + requestOptions, + ) + + /** @see [create] */ fun create(params: PermissionCreateParams): CompletableFuture = create(params, RequestOptions.none()) @@ -41,8 +59,26 @@ interface PermissionServiceAsync { * Organization owners can use this endpoint to view all permissions for a fine-tuned model * checkpoint. */ - fun retrieve(params: PermissionRetrieveParams): CompletableFuture = - retrieve(params, RequestOptions.none()) + fun retrieve(fineTunedModelCheckpoint: String): CompletableFuture = + retrieve(fineTunedModelCheckpoint, PermissionRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + fineTunedModelCheckpoint: String, + params: PermissionRetrieveParams = PermissionRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve( + params.toBuilder().fineTunedModelCheckpoint(fineTunedModelCheckpoint).build(), + requestOptions, + ) + + /** @see [retrieve] */ + fun retrieve( + fineTunedModelCheckpoint: String, + params: PermissionRetrieveParams = PermissionRetrieveParams.none(), + ): CompletableFuture = + retrieve(fineTunedModelCheckpoint, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -50,12 +86,38 @@ interface PermissionServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [retrieve] */ + fun retrieve(params: PermissionRetrieveParams): CompletableFuture = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + fineTunedModelCheckpoint: String, + requestOptions: RequestOptions, + ): CompletableFuture = + retrieve(fineTunedModelCheckpoint, PermissionRetrieveParams.none(), requestOptions) + /** * **NOTE:** This endpoint requires an [admin API key](../admin-api-keys). * * Organization owners can use this endpoint to delete a permission for a fine-tuned model * checkpoint. */ + fun delete( + permissionId: String, + params: PermissionDeleteParams, + ): CompletableFuture = + delete(permissionId, params, RequestOptions.none()) + + /** @see [delete] */ + fun delete( + permissionId: String, + params: PermissionDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().permissionId(permissionId).build(), requestOptions) + + /** @see [delete] */ fun delete(params: PermissionDeleteParams): CompletableFuture = delete(params, RequestOptions.none()) @@ -77,6 +139,26 @@ interface PermissionServiceAsync { * same as [PermissionServiceAsync.create]. */ @MustBeClosed + fun create( + fineTunedModelCheckpoint: String, + params: PermissionCreateParams, + ): CompletableFuture> = + create(fineTunedModelCheckpoint, params, RequestOptions.none()) + + /** @see [create] */ + @MustBeClosed + fun create( + fineTunedModelCheckpoint: String, + params: PermissionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + create( + params.toBuilder().fineTunedModelCheckpoint(fineTunedModelCheckpoint).build(), + requestOptions, + ) + + /** @see [create] */ + @MustBeClosed fun create( params: PermissionCreateParams ): CompletableFuture> = @@ -96,9 +178,29 @@ interface PermissionServiceAsync { */ @MustBeClosed fun retrieve( - params: PermissionRetrieveParams + fineTunedModelCheckpoint: String ): CompletableFuture> = - retrieve(params, RequestOptions.none()) + retrieve(fineTunedModelCheckpoint, PermissionRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fineTunedModelCheckpoint: String, + params: PermissionRetrieveParams = PermissionRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve( + params.toBuilder().fineTunedModelCheckpoint(fineTunedModelCheckpoint).build(), + requestOptions, + ) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fineTunedModelCheckpoint: String, + params: PermissionRetrieveParams = PermissionRetrieveParams.none(), + ): CompletableFuture> = + retrieve(fineTunedModelCheckpoint, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -107,12 +209,44 @@ interface PermissionServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + params: PermissionRetrieveParams + ): CompletableFuture> = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fineTunedModelCheckpoint: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + retrieve(fineTunedModelCheckpoint, PermissionRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `delete * /fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions/{permission_id}`, but * is otherwise the same as [PermissionServiceAsync.delete]. */ @MustBeClosed + fun delete( + permissionId: String, + params: PermissionDeleteParams, + ): CompletableFuture> = + delete(permissionId, params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + permissionId: String, + params: PermissionDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + delete(params.toBuilder().permissionId(permissionId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed fun delete( params: PermissionDeleteParams ): CompletableFuture> = diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/checkpoints/PermissionServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/checkpoints/PermissionServiceAsyncImpl.kt index 9d763dd7..0d3bf5c0 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/checkpoints/PermissionServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/checkpoints/PermissionServiceAsyncImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.async.finetuning.checkpoints import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -23,6 +24,7 @@ import com.openai.models.finetuning.checkpoints.permissions.PermissionDeleteResp import com.openai.models.finetuning.checkpoints.permissions.PermissionRetrieveParams import com.openai.models.finetuning.checkpoints.permissions.PermissionRetrieveResponse import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class PermissionServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : PermissionServiceAsync { @@ -67,6 +69,9 @@ class PermissionServiceAsyncImpl internal constructor(private val clientOptions: params: PermissionCreateParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTunedModelCheckpoint", params.fineTunedModelCheckpoint().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -94,6 +99,7 @@ class PermissionServiceAsyncImpl internal constructor(private val clientOptions: .let { PermissionCreatePageAsync.builder() .service(PermissionServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() @@ -110,6 +116,9 @@ class PermissionServiceAsyncImpl internal constructor(private val clientOptions: params: PermissionRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTunedModelCheckpoint", params.fineTunedModelCheckpoint().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -145,6 +154,9 @@ class PermissionServiceAsyncImpl internal constructor(private val clientOptions: params: PermissionDeleteParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("permissionId", params.permissionId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) 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 a479aa81..b53b92a9 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 @@ -17,8 +17,23 @@ interface CheckpointServiceAsync { fun withRawResponse(): WithRawResponse /** List checkpoints for a fine-tuning job. */ - fun list(params: CheckpointListParams): CompletableFuture = - list(params, RequestOptions.none()) + fun list(fineTuningJobId: String): CompletableFuture = + list(fineTuningJobId, CheckpointListParams.none()) + + /** @see [list] */ + fun list( + fineTuningJobId: String, + params: CheckpointListParams = CheckpointListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + list(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [list] */ + fun list( + fineTuningJobId: String, + params: CheckpointListParams = CheckpointListParams.none(), + ): CompletableFuture = + list(fineTuningJobId, params, RequestOptions.none()) /** @see [list] */ fun list( @@ -26,6 +41,17 @@ interface CheckpointServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [list] */ + fun list(params: CheckpointListParams): CompletableFuture = + list(params, RequestOptions.none()) + + /** @see [list] */ + fun list( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + list(fineTuningJobId, CheckpointListParams.none(), requestOptions) + /** * A view of [CheckpointServiceAsync] that provides access to raw HTTP responses for each * method. @@ -38,9 +64,26 @@ interface CheckpointServiceAsync { */ @MustBeClosed fun list( - params: CheckpointListParams + fineTuningJobId: String ): CompletableFuture> = - list(params, RequestOptions.none()) + list(fineTuningJobId, CheckpointListParams.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + fineTuningJobId: String, + params: CheckpointListParams = CheckpointListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + list(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed + fun list( + fineTuningJobId: String, + params: CheckpointListParams = CheckpointListParams.none(), + ): CompletableFuture> = + list(fineTuningJobId, params, RequestOptions.none()) /** @see [list] */ @MustBeClosed @@ -48,5 +91,20 @@ interface CheckpointServiceAsync { params: CheckpointListParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + + /** @see [list] */ + @MustBeClosed + fun list( + params: CheckpointListParams + ): CompletableFuture> = + list(params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + list(fineTuningJobId, CheckpointListParams.none(), requestOptions) } } 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 61014509..e4f74235 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 @@ -4,6 +4,7 @@ package com.openai.services.async.finetuning.jobs import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -18,6 +19,7 @@ import com.openai.models.finetuning.jobs.checkpoints.CheckpointListPageAsync import com.openai.models.finetuning.jobs.checkpoints.CheckpointListPageResponse import com.openai.models.finetuning.jobs.checkpoints.CheckpointListParams import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class CheckpointServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : CheckpointServiceAsync { @@ -48,6 +50,9 @@ class CheckpointServiceAsyncImpl internal constructor(private val clientOptions: params: CheckpointListParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTuningJobId", params.fineTuningJobId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -69,6 +74,7 @@ class CheckpointServiceAsyncImpl internal constructor(private val clientOptions: .let { CheckpointListPageAsync.builder() .service(CheckpointServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/graders/GraderModelServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/graders/GraderModelServiceAsync.kt new file mode 100644 index 00000000..e1d62600 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/graders/GraderModelServiceAsync.kt @@ -0,0 +1,17 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.graders + +interface GraderModelServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * A view of [GraderModelServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/graders/GraderModelServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/graders/GraderModelServiceAsyncImpl.kt new file mode 100644 index 00000000..8132d3b8 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/graders/GraderModelServiceAsyncImpl.kt @@ -0,0 +1,18 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.graders + +import com.openai.core.ClientOptions + +class GraderModelServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + GraderModelServiceAsync { + + private val withRawResponse: GraderModelServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): GraderModelServiceAsync.WithRawResponse = withRawResponse + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + GraderModelServiceAsync.WithRawResponse +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/responses/InputItemServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/responses/InputItemServiceAsync.kt index c0c1abfd..11b92dbf 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/responses/InputItemServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/responses/InputItemServiceAsync.kt @@ -17,8 +17,22 @@ interface InputItemServiceAsync { fun withRawResponse(): WithRawResponse /** Returns a list of input items for a given response. */ - fun list(params: InputItemListParams): CompletableFuture = - list(params, RequestOptions.none()) + fun list(responseId: String): CompletableFuture = + list(responseId, InputItemListParams.none()) + + /** @see [list] */ + fun list( + responseId: String, + params: InputItemListParams = InputItemListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + list(params.toBuilder().responseId(responseId).build(), requestOptions) + + /** @see [list] */ + fun list( + responseId: String, + params: InputItemListParams = InputItemListParams.none(), + ): CompletableFuture = list(responseId, params, RequestOptions.none()) /** @see [list] */ fun list( @@ -26,6 +40,17 @@ interface InputItemServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [list] */ + fun list(params: InputItemListParams): CompletableFuture = + list(params, RequestOptions.none()) + + /** @see [list] */ + fun list( + responseId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + list(responseId, InputItemListParams.none(), requestOptions) + /** * A view of [InputItemServiceAsync] that provides access to raw HTTP responses for each method. */ @@ -36,10 +61,25 @@ interface InputItemServiceAsync { * otherwise the same as [InputItemServiceAsync.list]. */ @MustBeClosed + fun list(responseId: String): CompletableFuture> = + list(responseId, InputItemListParams.none()) + + /** @see [list] */ + @MustBeClosed fun list( - params: InputItemListParams + responseId: String, + params: InputItemListParams = InputItemListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> = - list(params, RequestOptions.none()) + list(params.toBuilder().responseId(responseId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed + fun list( + responseId: String, + params: InputItemListParams = InputItemListParams.none(), + ): CompletableFuture> = + list(responseId, params, RequestOptions.none()) /** @see [list] */ @MustBeClosed @@ -47,5 +87,20 @@ interface InputItemServiceAsync { params: InputItemListParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + + /** @see [list] */ + @MustBeClosed + fun list( + params: InputItemListParams + ): CompletableFuture> = + list(params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + responseId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + list(responseId, InputItemListParams.none(), requestOptions) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/responses/InputItemServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/responses/InputItemServiceAsyncImpl.kt index 32f8112e..0dbd0e04 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/responses/InputItemServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/responses/InputItemServiceAsyncImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.async.responses import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -18,6 +19,7 @@ import com.openai.models.responses.inputitems.InputItemListPageAsync import com.openai.models.responses.inputitems.InputItemListParams import com.openai.models.responses.inputitems.ResponseItemList import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class InputItemServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : InputItemServiceAsync { @@ -47,6 +49,9 @@ class InputItemServiceAsyncImpl internal constructor(private val clientOptions: params: InputItemListParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("responseId", params.responseId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -68,6 +73,7 @@ class InputItemServiceAsyncImpl internal constructor(private val clientOptions: .let { InputItemListPageAsync.builder() .service(InputItemServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() 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 2fd89686..7961d9b6 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 @@ -28,6 +28,18 @@ interface PartServiceAsync { * Parts when you * [complete the Upload](https://platform.openai.com/docs/api-reference/uploads/complete). */ + fun create(uploadId: String, params: PartCreateParams): CompletableFuture = + create(uploadId, params, RequestOptions.none()) + + /** @see [create] */ + fun create( + uploadId: String, + params: PartCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + create(params.toBuilder().uploadId(uploadId).build(), requestOptions) + + /** @see [create] */ fun create(params: PartCreateParams): CompletableFuture = create(params, RequestOptions.none()) @@ -45,6 +57,23 @@ interface PartServiceAsync { * same as [PartServiceAsync.create]. */ @MustBeClosed + fun create( + uploadId: String, + params: PartCreateParams, + ): CompletableFuture> = + create(uploadId, params, RequestOptions.none()) + + /** @see [create] */ + @MustBeClosed + fun create( + uploadId: String, + params: PartCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + create(params.toBuilder().uploadId(uploadId).build(), requestOptions) + + /** @see [create] */ + @MustBeClosed fun create(params: PartCreateParams): CompletableFuture> = create(params, RequestOptions.none()) 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 6fddaa5a..381fb5fb 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 @@ -4,6 +4,7 @@ package com.openai.services.async.uploads import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -18,6 +19,7 @@ import com.openai.models.ErrorObject import com.openai.models.uploads.parts.PartCreateParams import com.openai.models.uploads.parts.UploadPart import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class PartServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : PartServiceAsync { @@ -47,6 +49,9 @@ class PartServiceAsyncImpl internal constructor(private val clientOptions: Clien params: PartCreateParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("uploadId", params.uploadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileBatchServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileBatchServiceAsync.kt index 9eb12653..df0a4a2d 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileBatchServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileBatchServiceAsync.kt @@ -21,6 +21,21 @@ interface FileBatchServiceAsync { fun withRawResponse(): WithRawResponse /** Create a vector store file batch. */ + fun create( + vectorStoreId: String, + params: FileBatchCreateParams, + ): CompletableFuture = + create(vectorStoreId, params, RequestOptions.none()) + + /** @see [create] */ + fun create( + vectorStoreId: String, + params: FileBatchCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + create(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [create] */ fun create(params: FileBatchCreateParams): CompletableFuture = create(params, RequestOptions.none()) @@ -31,6 +46,20 @@ interface FileBatchServiceAsync { ): CompletableFuture /** Retrieves a vector store file batch. */ + fun retrieve( + batchId: String, + params: FileBatchRetrieveParams, + ): CompletableFuture = retrieve(batchId, params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + batchId: String, + params: FileBatchRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [retrieve] */ fun retrieve(params: FileBatchRetrieveParams): CompletableFuture = retrieve(params, RequestOptions.none()) @@ -44,6 +73,20 @@ interface FileBatchServiceAsync { * Cancel a vector store file batch. This attempts to cancel the processing of files in this * batch as soon as possible. */ + fun cancel( + batchId: String, + params: FileBatchCancelParams, + ): CompletableFuture = cancel(batchId, params, RequestOptions.none()) + + /** @see [cancel] */ + fun cancel( + batchId: String, + params: FileBatchCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + cancel(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [cancel] */ fun cancel(params: FileBatchCancelParams): CompletableFuture = cancel(params, RequestOptions.none()) @@ -54,6 +97,21 @@ interface FileBatchServiceAsync { ): CompletableFuture /** Returns a list of vector store files in a batch. */ + fun listFiles( + batchId: String, + params: FileBatchListFilesParams, + ): CompletableFuture = + listFiles(batchId, params, RequestOptions.none()) + + /** @see [listFiles] */ + fun listFiles( + batchId: String, + params: FileBatchListFilesParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + listFiles(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [listFiles] */ fun listFiles( params: FileBatchListFilesParams ): CompletableFuture = listFiles(params, RequestOptions.none()) @@ -74,6 +132,23 @@ interface FileBatchServiceAsync { * is otherwise the same as [FileBatchServiceAsync.create]. */ @MustBeClosed + fun create( + vectorStoreId: String, + params: FileBatchCreateParams, + ): CompletableFuture> = + create(vectorStoreId, params, RequestOptions.none()) + + /** @see [create] */ + @MustBeClosed + fun create( + vectorStoreId: String, + params: FileBatchCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + create(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [create] */ + @MustBeClosed fun create( params: FileBatchCreateParams ): CompletableFuture> = @@ -92,6 +167,23 @@ interface FileBatchServiceAsync { * [FileBatchServiceAsync.retrieve]. */ @MustBeClosed + fun retrieve( + batchId: String, + params: FileBatchRetrieveParams, + ): CompletableFuture> = + retrieve(batchId, params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + batchId: String, + params: FileBatchRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve( params: FileBatchRetrieveParams ): CompletableFuture> = @@ -110,6 +202,23 @@ interface FileBatchServiceAsync { * same as [FileBatchServiceAsync.cancel]. */ @MustBeClosed + fun cancel( + batchId: String, + params: FileBatchCancelParams, + ): CompletableFuture> = + cancel(batchId, params, RequestOptions.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + batchId: String, + params: FileBatchCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + cancel(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [cancel] */ + @MustBeClosed fun cancel( params: FileBatchCancelParams ): CompletableFuture> = @@ -128,6 +237,23 @@ interface FileBatchServiceAsync { * same as [FileBatchServiceAsync.listFiles]. */ @MustBeClosed + fun listFiles( + batchId: String, + params: FileBatchListFilesParams, + ): CompletableFuture> = + listFiles(batchId, params, RequestOptions.none()) + + /** @see [listFiles] */ + @MustBeClosed + fun listFiles( + batchId: String, + params: FileBatchListFilesParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + listFiles(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [listFiles] */ + @MustBeClosed fun listFiles( params: FileBatchListFilesParams ): CompletableFuture> = diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileBatchServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileBatchServiceAsyncImpl.kt index e9d7625a..1d8d8086 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileBatchServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileBatchServiceAsyncImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.async.vectorstores import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -24,6 +25,7 @@ import com.openai.models.vectorstores.filebatches.FileBatchListFilesParams import com.openai.models.vectorstores.filebatches.FileBatchRetrieveParams import com.openai.models.vectorstores.filebatches.VectorStoreFileBatch import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class FileBatchServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : FileBatchServiceAsync { @@ -80,6 +82,9 @@ class FileBatchServiceAsyncImpl internal constructor(private val clientOptions: params: FileBatchCreateParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("vectorStoreId", params.vectorStoreId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -112,6 +117,9 @@ class FileBatchServiceAsyncImpl internal constructor(private val clientOptions: params: FileBatchRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("batchId", params.batchId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -148,6 +156,9 @@ class FileBatchServiceAsyncImpl internal constructor(private val clientOptions: params: FileBatchCancelParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("batchId", params.batchId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -186,6 +197,9 @@ class FileBatchServiceAsyncImpl internal constructor(private val clientOptions: params: FileBatchListFilesParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("batchId", params.batchId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -214,6 +228,7 @@ class FileBatchServiceAsyncImpl internal constructor(private val clientOptions: .let { FileBatchListFilesPageAsync.builder() .service(FileBatchServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileServiceAsync.kt index 27a654c3..bc0b1e49 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileServiceAsync.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileServiceAsync.kt @@ -29,6 +29,20 @@ interface FileServiceAsync { * [File](https://platform.openai.com/docs/api-reference/files) to a * [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object). */ + fun create( + vectorStoreId: String, + params: FileCreateParams, + ): CompletableFuture = create(vectorStoreId, params, RequestOptions.none()) + + /** @see [create] */ + fun create( + vectorStoreId: String, + params: FileCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + create(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [create] */ fun create(params: FileCreateParams): CompletableFuture = create(params, RequestOptions.none()) @@ -39,6 +53,18 @@ interface FileServiceAsync { ): CompletableFuture /** Retrieves a vector store file. */ + fun retrieve(fileId: String, params: FileRetrieveParams): CompletableFuture = + retrieve(fileId, params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + fileId: String, + params: FileRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [retrieve] */ fun retrieve(params: FileRetrieveParams): CompletableFuture = retrieve(params, RequestOptions.none()) @@ -49,6 +75,18 @@ interface FileServiceAsync { ): CompletableFuture /** Update attributes on a vector store file. */ + fun update(fileId: String, params: FileUpdateParams): CompletableFuture = + update(fileId, params, RequestOptions.none()) + + /** @see [update] */ + fun update( + fileId: String, + params: FileUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + update(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [update] */ fun update(params: FileUpdateParams): CompletableFuture = update(params, RequestOptions.none()) @@ -59,8 +97,22 @@ interface FileServiceAsync { ): CompletableFuture /** Returns a list of vector store files. */ - fun list(params: FileListParams): CompletableFuture = - list(params, RequestOptions.none()) + fun list(vectorStoreId: String): CompletableFuture = + list(vectorStoreId, FileListParams.none()) + + /** @see [list] */ + fun list( + vectorStoreId: String, + params: FileListParams = FileListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + list(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [list] */ + fun list( + vectorStoreId: String, + params: FileListParams = FileListParams.none(), + ): CompletableFuture = list(vectorStoreId, params, RequestOptions.none()) /** @see [list] */ fun list( @@ -68,11 +120,36 @@ interface FileServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** @see [list] */ + fun list(params: FileListParams): CompletableFuture = + list(params, RequestOptions.none()) + + /** @see [list] */ + fun list( + vectorStoreId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + list(vectorStoreId, FileListParams.none(), requestOptions) + /** * 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. */ + fun delete( + fileId: String, + params: FileDeleteParams, + ): CompletableFuture = delete(fileId, params, RequestOptions.none()) + + /** @see [delete] */ + fun delete( + fileId: String, + params: FileDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [delete] */ fun delete(params: FileDeleteParams): CompletableFuture = delete(params, RequestOptions.none()) @@ -83,6 +160,20 @@ interface FileServiceAsync { ): CompletableFuture /** Retrieve the parsed contents of a vector store file. */ + fun content( + fileId: String, + params: FileContentParams, + ): CompletableFuture = content(fileId, params, RequestOptions.none()) + + /** @see [content] */ + fun content( + fileId: String, + params: FileContentParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + content(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [content] */ fun content(params: FileContentParams): CompletableFuture = content(params, RequestOptions.none()) @@ -100,6 +191,23 @@ interface FileServiceAsync { * otherwise the same as [FileServiceAsync.create]. */ @MustBeClosed + fun create( + vectorStoreId: String, + params: FileCreateParams, + ): CompletableFuture> = + create(vectorStoreId, params, RequestOptions.none()) + + /** @see [create] */ + @MustBeClosed + fun create( + vectorStoreId: String, + params: FileCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + create(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [create] */ + @MustBeClosed fun create(params: FileCreateParams): CompletableFuture> = create(params, RequestOptions.none()) @@ -115,6 +223,23 @@ interface FileServiceAsync { * but is otherwise the same as [FileServiceAsync.retrieve]. */ @MustBeClosed + fun retrieve( + fileId: String, + params: FileRetrieveParams, + ): CompletableFuture> = + retrieve(fileId, params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fileId: String, + params: FileRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve( params: FileRetrieveParams ): CompletableFuture> = @@ -132,6 +257,23 @@ interface FileServiceAsync { * but is otherwise the same as [FileServiceAsync.update]. */ @MustBeClosed + fun update( + fileId: String, + params: FileUpdateParams, + ): CompletableFuture> = + update(fileId, params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + fileId: String, + params: FileUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + update(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed fun update(params: FileUpdateParams): CompletableFuture> = update(params, RequestOptions.none()) @@ -147,8 +289,25 @@ interface FileServiceAsync { * otherwise the same as [FileServiceAsync.list]. */ @MustBeClosed - fun list(params: FileListParams): CompletableFuture> = - list(params, RequestOptions.none()) + fun list(vectorStoreId: String): CompletableFuture> = + list(vectorStoreId, FileListParams.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + vectorStoreId: String, + params: FileListParams = FileListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + list(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed + fun list( + vectorStoreId: String, + params: FileListParams = FileListParams.none(), + ): CompletableFuture> = + list(vectorStoreId, params, RequestOptions.none()) /** @see [list] */ @MustBeClosed @@ -157,12 +316,42 @@ interface FileServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** @see [list] */ + @MustBeClosed + fun list(params: FileListParams): CompletableFuture> = + list(params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + vectorStoreId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + list(vectorStoreId, FileListParams.none(), requestOptions) + /** * Returns a raw HTTP response for `delete * /vector_stores/{vector_store_id}/files/{file_id}`, but is otherwise the same as * [FileServiceAsync.delete]. */ @MustBeClosed + fun delete( + fileId: String, + params: FileDeleteParams, + ): CompletableFuture> = + delete(fileId, params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + fileId: String, + params: FileDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + delete(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed fun delete( params: FileDeleteParams ): CompletableFuture> = @@ -181,6 +370,23 @@ interface FileServiceAsync { * [FileServiceAsync.content]. */ @MustBeClosed + fun content( + fileId: String, + params: FileContentParams, + ): CompletableFuture> = + content(fileId, params, RequestOptions.none()) + + /** @see [content] */ + @MustBeClosed + fun content( + fileId: String, + params: FileContentParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + content(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [content] */ + @MustBeClosed fun content( params: FileContentParams ): CompletableFuture> = diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileServiceAsyncImpl.kt index a03202c6..9780e9b6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileServiceAsyncImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileServiceAsyncImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.async.vectorstores import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -29,6 +30,7 @@ import com.openai.models.vectorstores.files.FileUpdateParams import com.openai.models.vectorstores.files.VectorStoreFile import com.openai.models.vectorstores.files.VectorStoreFileDeleted import java.util.concurrent.CompletableFuture +import kotlin.jvm.optionals.getOrNull class FileServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : FileServiceAsync { @@ -98,6 +100,9 @@ class FileServiceAsyncImpl internal constructor(private val clientOptions: Clien params: FileCreateParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("vectorStoreId", params.vectorStoreId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -129,6 +134,9 @@ class FileServiceAsyncImpl internal constructor(private val clientOptions: Clien params: FileRetrieveParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fileId", params.fileId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -164,6 +172,9 @@ class FileServiceAsyncImpl internal constructor(private val clientOptions: Clien params: FileUpdateParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fileId", params.fileId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -201,6 +212,9 @@ class FileServiceAsyncImpl internal constructor(private val clientOptions: Clien params: FileListParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("vectorStoreId", params.vectorStoreId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -223,6 +237,7 @@ class FileServiceAsyncImpl internal constructor(private val clientOptions: Clien .let { FileListPageAsync.builder() .service(FileServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() @@ -239,6 +254,9 @@ class FileServiceAsyncImpl internal constructor(private val clientOptions: Clien params: FileDeleteParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fileId", params.fileId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) @@ -276,6 +294,9 @@ class FileServiceAsyncImpl internal constructor(private val clientOptions: Clien params: FileContentParams, requestOptions: RequestOptions, ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fileId", params.fileId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -304,6 +325,7 @@ class FileServiceAsyncImpl internal constructor(private val clientOptions: Clien .let { FileContentPageAsync.builder() .service(FileServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() 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 6c66bb58..778efa58 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 @@ -29,7 +29,18 @@ interface BatchService { ): Batch /** Retrieves a batch. */ - fun retrieve(params: BatchRetrieveParams): Batch = retrieve(params, RequestOptions.none()) + fun retrieve(batchId: String): Batch = retrieve(batchId, BatchRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + batchId: String, + params: BatchRetrieveParams = BatchRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): Batch = retrieve(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve(batchId: String, params: BatchRetrieveParams = BatchRetrieveParams.none()): Batch = + retrieve(batchId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -37,6 +48,13 @@ interface BatchService { requestOptions: RequestOptions = RequestOptions.none(), ): Batch + /** @see [retrieve] */ + fun retrieve(params: BatchRetrieveParams): Batch = retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve(batchId: String, requestOptions: RequestOptions): Batch = + retrieve(batchId, BatchRetrieveParams.none(), requestOptions) + /** List your organization's batches. */ fun list(): BatchListPage = list(BatchListParams.none()) @@ -59,7 +77,18 @@ interface BatchService { * before changing to `cancelled`, where it will have partial results (if any) available in the * output file. */ - fun cancel(params: BatchCancelParams): Batch = cancel(params, RequestOptions.none()) + fun cancel(batchId: String): Batch = cancel(batchId, BatchCancelParams.none()) + + /** @see [cancel] */ + fun cancel( + batchId: String, + params: BatchCancelParams = BatchCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): Batch = cancel(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [cancel] */ + fun cancel(batchId: String, params: BatchCancelParams = BatchCancelParams.none()): Batch = + cancel(batchId, params, RequestOptions.none()) /** @see [cancel] */ fun cancel( @@ -67,6 +96,13 @@ interface BatchService { requestOptions: RequestOptions = RequestOptions.none(), ): Batch + /** @see [cancel] */ + fun cancel(params: BatchCancelParams): Batch = cancel(params, RequestOptions.none()) + + /** @see [cancel] */ + fun cancel(batchId: String, requestOptions: RequestOptions): Batch = + cancel(batchId, BatchCancelParams.none(), requestOptions) + /** A view of [BatchService] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -90,8 +126,24 @@ interface BatchService { * [BatchService.retrieve]. */ @MustBeClosed - fun retrieve(params: BatchRetrieveParams): HttpResponseFor = - retrieve(params, RequestOptions.none()) + fun retrieve(batchId: String): HttpResponseFor = + retrieve(batchId, BatchRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + batchId: String, + params: BatchRetrieveParams = BatchRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + batchId: String, + params: BatchRetrieveParams = BatchRetrieveParams.none(), + ): HttpResponseFor = retrieve(batchId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -100,6 +152,16 @@ interface BatchService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: BatchRetrieveParams): HttpResponseFor = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(batchId: String, requestOptions: RequestOptions): HttpResponseFor = + retrieve(batchId, BatchRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /batches`, but is otherwise the same as * [BatchService.list]. @@ -128,8 +190,24 @@ interface BatchService { * same as [BatchService.cancel]. */ @MustBeClosed - fun cancel(params: BatchCancelParams): HttpResponseFor = - cancel(params, RequestOptions.none()) + fun cancel(batchId: String): HttpResponseFor = + cancel(batchId, BatchCancelParams.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + batchId: String, + params: BatchCancelParams = BatchCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + cancel(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + batchId: String, + params: BatchCancelParams = BatchCancelParams.none(), + ): HttpResponseFor = cancel(batchId, params, RequestOptions.none()) /** @see [cancel] */ @MustBeClosed @@ -137,5 +215,15 @@ interface BatchService { params: BatchCancelParams, requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + + /** @see [cancel] */ + @MustBeClosed + fun cancel(params: BatchCancelParams): HttpResponseFor = + cancel(params, RequestOptions.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel(batchId: String, requestOptions: RequestOptions): HttpResponseFor = + cancel(batchId, BatchCancelParams.none(), requestOptions) } } 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 7a1a929c..51717e40 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 @@ -4,6 +4,7 @@ package com.openai.services.blocking import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -22,6 +23,7 @@ import com.openai.models.batches.BatchListPage import com.openai.models.batches.BatchListPageResponse import com.openai.models.batches.BatchListParams import com.openai.models.batches.BatchRetrieveParams +import kotlin.jvm.optionals.getOrNull class BatchServiceImpl internal constructor(private val clientOptions: ClientOptions) : BatchService { @@ -87,6 +89,9 @@ class BatchServiceImpl internal constructor(private val clientOptions: ClientOpt params: BatchRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("batchId", params.batchId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -147,6 +152,9 @@ class BatchServiceImpl internal constructor(private val clientOptions: ClientOpt params: BatchCancelParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("batchId", params.batchId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/EvalService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/EvalService.kt index 594a1ffc..c747c5ef 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/EvalService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/EvalService.kt @@ -28,10 +28,10 @@ interface EvalService { /** * Create the structure of an evaluation that can be used to test a model's performance. An - * evaluation is a set of testing criteria and a datasource. After creating an evaluation, you - * can run it on different models and model parameters. We support several types of graders and - * datasources. For more information, see the - * [Evals guide](https://platform.openai.com/docs/guides/evals). + * evaluation is a set of testing criteria and the config for a data source, which dictates the + * schema of the data used in the evaluation. After creating an evaluation, you can run it on + * different models and model parameters. We support several types of graders and datasources. + * For more information, see the [Evals guide](https://platform.openai.com/docs/guides/evals). */ fun create(params: EvalCreateParams): EvalCreateResponse = create(params, RequestOptions.none()) @@ -42,8 +42,20 @@ interface EvalService { ): EvalCreateResponse /** Get an evaluation by ID. */ - fun retrieve(params: EvalRetrieveParams): EvalRetrieveResponse = - retrieve(params, RequestOptions.none()) + fun retrieve(evalId: String): EvalRetrieveResponse = retrieve(evalId, EvalRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + evalId: String, + params: EvalRetrieveParams = EvalRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): EvalRetrieveResponse = retrieve(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + evalId: String, + params: EvalRetrieveParams = EvalRetrieveParams.none(), + ): EvalRetrieveResponse = retrieve(evalId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -51,8 +63,29 @@ interface EvalService { requestOptions: RequestOptions = RequestOptions.none(), ): EvalRetrieveResponse + /** @see [retrieve] */ + fun retrieve(params: EvalRetrieveParams): EvalRetrieveResponse = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve(evalId: String, requestOptions: RequestOptions): EvalRetrieveResponse = + retrieve(evalId, EvalRetrieveParams.none(), requestOptions) + /** Update certain properties of an evaluation. */ - fun update(params: EvalUpdateParams): EvalUpdateResponse = update(params, RequestOptions.none()) + fun update(evalId: String): EvalUpdateResponse = update(evalId, EvalUpdateParams.none()) + + /** @see [update] */ + fun update( + evalId: String, + params: EvalUpdateParams = EvalUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): EvalUpdateResponse = update(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [update] */ + fun update( + evalId: String, + params: EvalUpdateParams = EvalUpdateParams.none(), + ): EvalUpdateResponse = update(evalId, params, RequestOptions.none()) /** @see [update] */ fun update( @@ -60,6 +93,13 @@ interface EvalService { requestOptions: RequestOptions = RequestOptions.none(), ): EvalUpdateResponse + /** @see [update] */ + fun update(params: EvalUpdateParams): EvalUpdateResponse = update(params, RequestOptions.none()) + + /** @see [update] */ + fun update(evalId: String, requestOptions: RequestOptions): EvalUpdateResponse = + update(evalId, EvalUpdateParams.none(), requestOptions) + /** List evaluations for a project. */ fun list(): EvalListPage = list(EvalListParams.none()) @@ -78,7 +118,20 @@ interface EvalService { list(EvalListParams.none(), requestOptions) /** Delete an evaluation. */ - fun delete(params: EvalDeleteParams): EvalDeleteResponse = delete(params, RequestOptions.none()) + fun delete(evalId: String): EvalDeleteResponse = delete(evalId, EvalDeleteParams.none()) + + /** @see [delete] */ + fun delete( + evalId: String, + params: EvalDeleteParams = EvalDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): EvalDeleteResponse = delete(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [delete] */ + fun delete( + evalId: String, + params: EvalDeleteParams = EvalDeleteParams.none(), + ): EvalDeleteResponse = delete(evalId, params, RequestOptions.none()) /** @see [delete] */ fun delete( @@ -86,6 +139,13 @@ interface EvalService { requestOptions: RequestOptions = RequestOptions.none(), ): EvalDeleteResponse + /** @see [delete] */ + fun delete(params: EvalDeleteParams): EvalDeleteResponse = delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete(evalId: String, requestOptions: RequestOptions): EvalDeleteResponse = + delete(evalId, EvalDeleteParams.none(), requestOptions) + /** A view of [EvalService] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -111,8 +171,24 @@ interface EvalService { * [EvalService.retrieve]. */ @MustBeClosed - fun retrieve(params: EvalRetrieveParams): HttpResponseFor = - retrieve(params, RequestOptions.none()) + fun retrieve(evalId: String): HttpResponseFor = + retrieve(evalId, EvalRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + evalId: String, + params: EvalRetrieveParams = EvalRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + evalId: String, + params: EvalRetrieveParams = EvalRetrieveParams.none(), + ): HttpResponseFor = retrieve(evalId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -121,13 +197,42 @@ interface EvalService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: EvalRetrieveParams): HttpResponseFor = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + evalId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + retrieve(evalId, EvalRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /evals/{eval_id}`, but is otherwise the same as * [EvalService.update]. */ @MustBeClosed - fun update(params: EvalUpdateParams): HttpResponseFor = - update(params, RequestOptions.none()) + fun update(evalId: String): HttpResponseFor = + update(evalId, EvalUpdateParams.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + evalId: String, + params: EvalUpdateParams = EvalUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + update(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed + fun update( + evalId: String, + params: EvalUpdateParams = EvalUpdateParams.none(), + ): HttpResponseFor = update(evalId, params, RequestOptions.none()) /** @see [update] */ @MustBeClosed @@ -136,6 +241,19 @@ interface EvalService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [update] */ + @MustBeClosed + fun update(params: EvalUpdateParams): HttpResponseFor = + update(params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + evalId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + update(evalId, EvalUpdateParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /evals`, but is otherwise the same as * [EvalService.list]. @@ -164,8 +282,24 @@ interface EvalService { * [EvalService.delete]. */ @MustBeClosed - fun delete(params: EvalDeleteParams): HttpResponseFor = - delete(params, RequestOptions.none()) + fun delete(evalId: String): HttpResponseFor = + delete(evalId, EvalDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + evalId: String, + params: EvalDeleteParams = EvalDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + delete(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + evalId: String, + params: EvalDeleteParams = EvalDeleteParams.none(), + ): HttpResponseFor = delete(evalId, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -173,5 +307,18 @@ interface EvalService { params: EvalDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + + /** @see [delete] */ + @MustBeClosed + fun delete(params: EvalDeleteParams): HttpResponseFor = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + evalId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + delete(evalId, EvalDeleteParams.none(), requestOptions) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/EvalServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/EvalServiceImpl.kt index faa4cc5f..9fda99b9 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/EvalServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/EvalServiceImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.blocking import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -28,6 +29,7 @@ import com.openai.models.evals.EvalUpdateParams import com.openai.models.evals.EvalUpdateResponse import com.openai.services.blocking.evals.RunService import com.openai.services.blocking.evals.RunServiceImpl +import kotlin.jvm.optionals.getOrNull class EvalServiceImpl internal constructor(private val clientOptions: ClientOptions) : EvalService { @@ -119,6 +121,9 @@ class EvalServiceImpl internal constructor(private val clientOptions: ClientOpti params: EvalRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("evalId", params.evalId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -145,6 +150,9 @@ class EvalServiceImpl internal constructor(private val clientOptions: ClientOpti params: EvalUpdateParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("evalId", params.evalId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -206,6 +214,9 @@ class EvalServiceImpl internal constructor(private val clientOptions: ClientOpti params: EvalDeleteParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("evalId", params.evalId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) 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 c952d6af..3f4934c3 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 @@ -50,7 +50,20 @@ interface FileService { ): FileObject /** Returns information about a specific file. */ - fun retrieve(params: FileRetrieveParams): FileObject = retrieve(params, RequestOptions.none()) + fun retrieve(fileId: String): FileObject = retrieve(fileId, FileRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + fileId: String, + params: FileRetrieveParams = FileRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): FileObject = retrieve(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + fileId: String, + params: FileRetrieveParams = FileRetrieveParams.none(), + ): FileObject = retrieve(fileId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -58,6 +71,13 @@ interface FileService { requestOptions: RequestOptions = RequestOptions.none(), ): FileObject + /** @see [retrieve] */ + fun retrieve(params: FileRetrieveParams): FileObject = retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve(fileId: String, requestOptions: RequestOptions): FileObject = + retrieve(fileId, FileRetrieveParams.none(), requestOptions) + /** Returns a list of files. */ fun list(): FileListPage = list(FileListParams.none()) @@ -76,7 +96,18 @@ interface FileService { list(FileListParams.none(), requestOptions) /** Delete a file. */ - fun delete(params: FileDeleteParams): FileDeleted = delete(params, RequestOptions.none()) + fun delete(fileId: String): FileDeleted = delete(fileId, FileDeleteParams.none()) + + /** @see [delete] */ + fun delete( + fileId: String, + params: FileDeleteParams = FileDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): FileDeleted = delete(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [delete] */ + fun delete(fileId: String, params: FileDeleteParams = FileDeleteParams.none()): FileDeleted = + delete(fileId, params, RequestOptions.none()) /** @see [delete] */ fun delete( @@ -84,9 +115,31 @@ interface FileService { requestOptions: RequestOptions = RequestOptions.none(), ): FileDeleted + /** @see [delete] */ + fun delete(params: FileDeleteParams): FileDeleted = delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete(fileId: String, requestOptions: RequestOptions): FileDeleted = + delete(fileId, FileDeleteParams.none(), requestOptions) + /** Returns the contents of the specified file. */ @MustBeClosed - fun content(params: FileContentParams): HttpResponse = content(params, RequestOptions.none()) + fun content(fileId: String): HttpResponse = content(fileId, FileContentParams.none()) + + /** @see [content] */ + @MustBeClosed + fun content( + fileId: String, + params: FileContentParams = FileContentParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse = content(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [content] */ + @MustBeClosed + fun content( + fileId: String, + params: FileContentParams = FileContentParams.none(), + ): HttpResponse = content(fileId, params, RequestOptions.none()) /** @see [content] */ @MustBeClosed @@ -95,6 +148,15 @@ interface FileService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponse + /** @see [content] */ + @MustBeClosed + fun content(params: FileContentParams): HttpResponse = content(params, RequestOptions.none()) + + /** @see [content] */ + @MustBeClosed + fun content(fileId: String, requestOptions: RequestOptions): HttpResponse = + content(fileId, FileContentParams.none(), requestOptions) + /** A view of [FileService] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -118,8 +180,24 @@ interface FileService { * [FileService.retrieve]. */ @MustBeClosed - fun retrieve(params: FileRetrieveParams): HttpResponseFor = - retrieve(params, RequestOptions.none()) + fun retrieve(fileId: String): HttpResponseFor = + retrieve(fileId, FileRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fileId: String, + params: FileRetrieveParams = FileRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fileId: String, + params: FileRetrieveParams = FileRetrieveParams.none(), + ): HttpResponseFor = retrieve(fileId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -128,6 +206,16 @@ interface FileService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: FileRetrieveParams): HttpResponseFor = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(fileId: String, requestOptions: RequestOptions): HttpResponseFor = + retrieve(fileId, FileRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /files`, but is otherwise the same as * [FileService.list]. @@ -156,8 +244,24 @@ interface FileService { * [FileService.delete]. */ @MustBeClosed - fun delete(params: FileDeleteParams): HttpResponseFor = - delete(params, RequestOptions.none()) + fun delete(fileId: String): HttpResponseFor = + delete(fileId, FileDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + fileId: String, + params: FileDeleteParams = FileDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + delete(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + fileId: String, + params: FileDeleteParams = FileDeleteParams.none(), + ): HttpResponseFor = delete(fileId, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -166,13 +270,37 @@ interface FileService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [delete] */ + @MustBeClosed + fun delete(params: FileDeleteParams): HttpResponseFor = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete(fileId: String, requestOptions: RequestOptions): HttpResponseFor = + delete(fileId, FileDeleteParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /files/{file_id}/content`, but is otherwise the same * as [FileService.content]. */ @MustBeClosed - fun content(params: FileContentParams): HttpResponse = - content(params, RequestOptions.none()) + fun content(fileId: String): HttpResponse = content(fileId, FileContentParams.none()) + + /** @see [content] */ + @MustBeClosed + fun content( + fileId: String, + params: FileContentParams = FileContentParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse = content(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [content] */ + @MustBeClosed + fun content( + fileId: String, + params: FileContentParams = FileContentParams.none(), + ): HttpResponse = content(fileId, params, RequestOptions.none()) /** @see [content] */ @MustBeClosed @@ -180,5 +308,15 @@ interface FileService { params: FileContentParams, requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponse + + /** @see [content] */ + @MustBeClosed + fun content(params: FileContentParams): HttpResponse = + content(params, RequestOptions.none()) + + /** @see [content] */ + @MustBeClosed + fun content(fileId: String, requestOptions: RequestOptions): HttpResponse = + content(fileId, FileContentParams.none(), requestOptions) } } 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 c9b1f1f7..0f997029 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 @@ -4,6 +4,7 @@ package com.openai.services.blocking import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -26,6 +27,7 @@ import com.openai.models.files.FileListPageResponse import com.openai.models.files.FileListParams import com.openai.models.files.FileObject import com.openai.models.files.FileRetrieveParams +import kotlin.jvm.optionals.getOrNull class FileServiceImpl internal constructor(private val clientOptions: ClientOptions) : FileService { @@ -94,6 +96,9 @@ class FileServiceImpl internal constructor(private val clientOptions: ClientOpti params: FileRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fileId", params.fileId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -154,6 +159,9 @@ class FileServiceImpl internal constructor(private val clientOptions: ClientOpti params: FileDeleteParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fileId", params.fileId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) @@ -178,6 +186,9 @@ class FileServiceImpl internal constructor(private val clientOptions: ClientOpti params: FileContentParams, requestOptions: RequestOptions, ): HttpResponse { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fileId", params.fileId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) 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 b6380655..77f45a35 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 @@ -2,8 +2,10 @@ package com.openai.services.blocking +import com.openai.services.blocking.finetuning.AlphaService import com.openai.services.blocking.finetuning.CheckpointService import com.openai.services.blocking.finetuning.JobService +import com.openai.services.blocking.finetuning.MethodService interface FineTuningService { @@ -12,15 +14,23 @@ interface FineTuningService { */ fun withRawResponse(): WithRawResponse + fun methods(): MethodService + fun jobs(): JobService fun checkpoints(): CheckpointService + fun alpha(): AlphaService + /** A view of [FineTuningService] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { + fun methods(): MethodService.WithRawResponse + fun jobs(): JobService.WithRawResponse fun checkpoints(): CheckpointService.WithRawResponse + + fun alpha(): AlphaService.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 afe68acc..0f8b31d3 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 @@ -3,10 +3,14 @@ package com.openai.services.blocking import com.openai.core.ClientOptions +import com.openai.services.blocking.finetuning.AlphaService +import com.openai.services.blocking.finetuning.AlphaServiceImpl import com.openai.services.blocking.finetuning.CheckpointService import com.openai.services.blocking.finetuning.CheckpointServiceImpl import com.openai.services.blocking.finetuning.JobService import com.openai.services.blocking.finetuning.JobServiceImpl +import com.openai.services.blocking.finetuning.MethodService +import com.openai.services.blocking.finetuning.MethodServiceImpl class FineTuningServiceImpl internal constructor(private val clientOptions: ClientOptions) : FineTuningService { @@ -15,19 +19,31 @@ class FineTuningServiceImpl internal constructor(private val clientOptions: Clie WithRawResponseImpl(clientOptions) } + private val methods: MethodService by lazy { MethodServiceImpl(clientOptions) } + private val jobs: JobService by lazy { JobServiceImpl(clientOptions) } private val checkpoints: CheckpointService by lazy { CheckpointServiceImpl(clientOptions) } + private val alpha: AlphaService by lazy { AlphaServiceImpl(clientOptions) } + override fun withRawResponse(): FineTuningService.WithRawResponse = withRawResponse + override fun methods(): MethodService = methods + override fun jobs(): JobService = jobs override fun checkpoints(): CheckpointService = checkpoints + override fun alpha(): AlphaService = alpha + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : FineTuningService.WithRawResponse { + private val methods: MethodService.WithRawResponse by lazy { + MethodServiceImpl.WithRawResponseImpl(clientOptions) + } + private val jobs: JobService.WithRawResponse by lazy { JobServiceImpl.WithRawResponseImpl(clientOptions) } @@ -36,8 +52,16 @@ class FineTuningServiceImpl internal constructor(private val clientOptions: Clie CheckpointServiceImpl.WithRawResponseImpl(clientOptions) } + private val alpha: AlphaService.WithRawResponse by lazy { + AlphaServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun methods(): MethodService.WithRawResponse = methods + override fun jobs(): JobService.WithRawResponse = jobs override fun checkpoints(): CheckpointService.WithRawResponse = checkpoints + + override fun alpha(): AlphaService.WithRawResponse = alpha } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/GraderService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/GraderService.kt new file mode 100644 index 00000000..2c9433a2 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/GraderService.kt @@ -0,0 +1,21 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking + +import com.openai.services.blocking.graders.GraderModelService + +interface GraderService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + fun graderModels(): GraderModelService + + /** A view of [GraderService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun graderModels(): GraderModelService.WithRawResponse + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/GraderServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/GraderServiceImpl.kt new file mode 100644 index 00000000..d6d74305 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/GraderServiceImpl.kt @@ -0,0 +1,31 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking + +import com.openai.core.ClientOptions +import com.openai.services.blocking.graders.GraderModelService +import com.openai.services.blocking.graders.GraderModelServiceImpl + +class GraderServiceImpl internal constructor(private val clientOptions: ClientOptions) : + GraderService { + + private val withRawResponse: GraderService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val graderModels: GraderModelService by lazy { GraderModelServiceImpl(clientOptions) } + + override fun withRawResponse(): GraderService.WithRawResponse = withRawResponse + + override fun graderModels(): GraderModelService = graderModels + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + GraderService.WithRawResponse { + + private val graderModels: GraderModelService.WithRawResponse by lazy { + GraderModelServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun graderModels(): GraderModelService.WithRawResponse = graderModels + } +} 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 7b34885a..d6550622 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 @@ -23,7 +23,18 @@ interface ModelService { * Retrieves a model instance, providing basic information about the model such as the owner and * permissioning. */ - fun retrieve(params: ModelRetrieveParams): Model = retrieve(params, RequestOptions.none()) + fun retrieve(model: String): Model = retrieve(model, ModelRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + model: String, + params: ModelRetrieveParams = ModelRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): Model = retrieve(params.toBuilder().model(model).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve(model: String, params: ModelRetrieveParams = ModelRetrieveParams.none()): Model = + retrieve(model, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -31,6 +42,13 @@ interface ModelService { requestOptions: RequestOptions = RequestOptions.none(), ): Model + /** @see [retrieve] */ + fun retrieve(params: ModelRetrieveParams): Model = retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve(model: String, requestOptions: RequestOptions): Model = + retrieve(model, ModelRetrieveParams.none(), requestOptions) + /** * Lists the currently available models, and provides basic information about each one such as * the owner and availability. @@ -55,7 +73,18 @@ interface ModelService { * Delete a fine-tuned model. You must have the Owner role in your organization to delete a * model. */ - fun delete(params: ModelDeleteParams): ModelDeleted = delete(params, RequestOptions.none()) + fun delete(model: String): ModelDeleted = delete(model, ModelDeleteParams.none()) + + /** @see [delete] */ + fun delete( + model: String, + params: ModelDeleteParams = ModelDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ModelDeleted = delete(params.toBuilder().model(model).build(), requestOptions) + + /** @see [delete] */ + fun delete(model: String, params: ModelDeleteParams = ModelDeleteParams.none()): ModelDeleted = + delete(model, params, RequestOptions.none()) /** @see [delete] */ fun delete( @@ -63,6 +92,13 @@ interface ModelService { requestOptions: RequestOptions = RequestOptions.none(), ): ModelDeleted + /** @see [delete] */ + fun delete(params: ModelDeleteParams): ModelDeleted = delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete(model: String, requestOptions: RequestOptions): ModelDeleted = + delete(model, ModelDeleteParams.none(), requestOptions) + /** A view of [ModelService] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -71,8 +107,24 @@ interface ModelService { * [ModelService.retrieve]. */ @MustBeClosed - fun retrieve(params: ModelRetrieveParams): HttpResponseFor = - retrieve(params, RequestOptions.none()) + fun retrieve(model: String): HttpResponseFor = + retrieve(model, ModelRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + model: String, + params: ModelRetrieveParams = ModelRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().model(model).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + model: String, + params: ModelRetrieveParams = ModelRetrieveParams.none(), + ): HttpResponseFor = retrieve(model, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -81,6 +133,16 @@ interface ModelService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: ModelRetrieveParams): HttpResponseFor = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(model: String, requestOptions: RequestOptions): HttpResponseFor = + retrieve(model, ModelRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /models`, but is otherwise the same as * [ModelService.list]. @@ -109,8 +171,24 @@ interface ModelService { * [ModelService.delete]. */ @MustBeClosed - fun delete(params: ModelDeleteParams): HttpResponseFor = - delete(params, RequestOptions.none()) + fun delete(model: String): HttpResponseFor = + delete(model, ModelDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + model: String, + params: ModelDeleteParams = ModelDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + delete(params.toBuilder().model(model).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + model: String, + params: ModelDeleteParams = ModelDeleteParams.none(), + ): HttpResponseFor = delete(model, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -118,5 +196,15 @@ interface ModelService { params: ModelDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + + /** @see [delete] */ + @MustBeClosed + fun delete(params: ModelDeleteParams): HttpResponseFor = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete(model: String, requestOptions: RequestOptions): HttpResponseFor = + delete(model, ModelDeleteParams.none(), requestOptions) } } 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 6e7a023e..7104c2ed 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 @@ -4,6 +4,7 @@ package com.openai.services.blocking import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -22,6 +23,7 @@ import com.openai.models.models.ModelListPage import com.openai.models.models.ModelListPageResponse import com.openai.models.models.ModelListParams import com.openai.models.models.ModelRetrieveParams +import kotlin.jvm.optionals.getOrNull class ModelServiceImpl internal constructor(private val clientOptions: ClientOptions) : ModelService { @@ -56,12 +58,15 @@ class ModelServiceImpl internal constructor(private val clientOptions: ClientOpt params: ModelRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("model", params.model().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) .addPathSegments("models", params._pathParam(0)) .build() - .prepare(clientOptions, params, params.model()) + .prepare(clientOptions, params, params.model().get()) val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) val response = clientOptions.httpClient.execute(request, requestOptions) return response.parseable { @@ -116,13 +121,16 @@ class ModelServiceImpl internal constructor(private val clientOptions: ClientOpt params: ModelDeleteParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("model", params.model().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) .addPathSegments("models", params._pathParam(0)) .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } .build() - .prepare(clientOptions, params, params.model()) + .prepare(clientOptions, params, params.model().get()) val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) val response = clientOptions.httpClient.execute(request, requestOptions) return response.parseable { diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ResponseService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ResponseService.kt index acced25c..12ed80dd 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ResponseService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ResponseService.kt @@ -12,6 +12,8 @@ import com.openai.models.responses.ResponseCreateParams import com.openai.models.responses.ResponseDeleteParams import com.openai.models.responses.ResponseRetrieveParams import com.openai.models.responses.ResponseStreamEvent +import com.openai.models.responses.StructuredResponse +import com.openai.models.responses.StructuredResponseCreateParams import com.openai.services.blocking.responses.InputItemService interface ResponseService { @@ -42,6 +44,27 @@ interface ResponseService { requestOptions: RequestOptions = RequestOptions.none(), ): Response + /** + * Creates a model response. The model's structured output in JSON form will be deserialized + * automatically into an instance of the class `T`. See the SDK documentation for more details. + * + * @see create + */ + fun create(params: StructuredResponseCreateParams): StructuredResponse = + create(params, RequestOptions.none()) + + /** + * Creates a model response. The model's structured output in JSON form will be deserialized + * automatically into an instance of the class `T`. See the SDK documentation for more details. + * + * @see create + */ + fun create( + params: StructuredResponseCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): StructuredResponse = + StructuredResponse(params.responseType, create(params.rawParams, requestOptions)) + /** * Creates a model response. Provide [text](https://platform.openai.com/docs/guides/text) or * [image](https://platform.openai.com/docs/guides/images) inputs to generate @@ -65,7 +88,20 @@ interface ResponseService { ): StreamResponse /** Retrieves a model response with the given ID. */ - fun retrieve(params: ResponseRetrieveParams): Response = retrieve(params, RequestOptions.none()) + fun retrieve(responseId: String): Response = retrieve(responseId, ResponseRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + responseId: String, + params: ResponseRetrieveParams = ResponseRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): Response = retrieve(params.toBuilder().responseId(responseId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + responseId: String, + params: ResponseRetrieveParams = ResponseRetrieveParams.none(), + ): Response = retrieve(responseId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -73,12 +109,37 @@ interface ResponseService { requestOptions: RequestOptions = RequestOptions.none(), ): Response + /** @see [retrieve] */ + fun retrieve(params: ResponseRetrieveParams): Response = retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve(responseId: String, requestOptions: RequestOptions): Response = + retrieve(responseId, ResponseRetrieveParams.none(), requestOptions) + /** Deletes a model response with the given ID. */ - fun delete(params: ResponseDeleteParams) = delete(params, RequestOptions.none()) + fun delete(responseId: String) = delete(responseId, ResponseDeleteParams.none()) + + /** @see [delete] */ + fun delete( + responseId: String, + params: ResponseDeleteParams = ResponseDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ) = delete(params.toBuilder().responseId(responseId).build(), requestOptions) + + /** @see [delete] */ + fun delete(responseId: String, params: ResponseDeleteParams = ResponseDeleteParams.none()) = + delete(responseId, params, RequestOptions.none()) /** @see [delete] */ fun delete(params: ResponseDeleteParams, requestOptions: RequestOptions = RequestOptions.none()) + /** @see [delete] */ + fun delete(params: ResponseDeleteParams) = delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete(responseId: String, requestOptions: RequestOptions) = + delete(responseId, ResponseDeleteParams.none(), requestOptions) + /** A view of [ResponseService] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -121,8 +182,24 @@ interface ResponseService { * as [ResponseService.retrieve]. */ @MustBeClosed - fun retrieve(params: ResponseRetrieveParams): HttpResponseFor = - retrieve(params, RequestOptions.none()) + fun retrieve(responseId: String): HttpResponseFor = + retrieve(responseId, ResponseRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + responseId: String, + params: ResponseRetrieveParams = ResponseRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().responseId(responseId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + responseId: String, + params: ResponseRetrieveParams = ResponseRetrieveParams.none(), + ): HttpResponseFor = retrieve(responseId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -131,13 +208,41 @@ interface ResponseService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: ResponseRetrieveParams): HttpResponseFor = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + responseId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + retrieve(responseId, ResponseRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `delete /responses/{response_id}`, but is otherwise the * same as [ResponseService.delete]. */ @MustBeClosed - fun delete(params: ResponseDeleteParams): HttpResponse = - delete(params, RequestOptions.none()) + fun delete(responseId: String): HttpResponse = + delete(responseId, ResponseDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + responseId: String, + params: ResponseDeleteParams = ResponseDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse = delete(params.toBuilder().responseId(responseId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + responseId: String, + params: ResponseDeleteParams = ResponseDeleteParams.none(), + ): HttpResponse = delete(responseId, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -145,5 +250,15 @@ interface ResponseService { params: ResponseDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponse + + /** @see [delete] */ + @MustBeClosed + fun delete(params: ResponseDeleteParams): HttpResponse = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete(responseId: String, requestOptions: RequestOptions): HttpResponse = + delete(responseId, ResponseDeleteParams.none(), requestOptions) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ResponseServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ResponseServiceImpl.kt index b683a7d6..de7f11be 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/ResponseServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ResponseServiceImpl.kt @@ -5,6 +5,7 @@ package com.openai.services.blocking import com.openai.core.ClientOptions import com.openai.core.JsonValue import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.emptyHandler import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler @@ -29,6 +30,7 @@ import com.openai.models.responses.ResponseRetrieveParams import com.openai.models.responses.ResponseStreamEvent import com.openai.services.blocking.responses.InputItemService import com.openai.services.blocking.responses.InputItemServiceImpl +import kotlin.jvm.optionals.getOrNull class ResponseServiceImpl internal constructor(private val clientOptions: ClientOptions) : ResponseService { @@ -151,6 +153,9 @@ class ResponseServiceImpl internal constructor(private val clientOptions: Client params: ResponseRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("responseId", params.responseId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -176,6 +181,9 @@ class ResponseServiceImpl internal constructor(private val clientOptions: Client params: ResponseDeleteParams, requestOptions: RequestOptions, ): HttpResponse { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("responseId", params.responseId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) 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 47938c0e..17bc71b0 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 @@ -49,7 +49,18 @@ interface UploadService { ): Upload /** Cancels the Upload. No Parts may be added after an Upload is cancelled. */ - fun cancel(params: UploadCancelParams): Upload = cancel(params, RequestOptions.none()) + fun cancel(uploadId: String): Upload = cancel(uploadId, UploadCancelParams.none()) + + /** @see [cancel] */ + fun cancel( + uploadId: String, + params: UploadCancelParams = UploadCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): Upload = cancel(params.toBuilder().uploadId(uploadId).build(), requestOptions) + + /** @see [cancel] */ + fun cancel(uploadId: String, params: UploadCancelParams = UploadCancelParams.none()): Upload = + cancel(uploadId, params, RequestOptions.none()) /** @see [cancel] */ fun cancel( @@ -57,6 +68,13 @@ interface UploadService { requestOptions: RequestOptions = RequestOptions.none(), ): Upload + /** @see [cancel] */ + fun cancel(params: UploadCancelParams): Upload = cancel(params, RequestOptions.none()) + + /** @see [cancel] */ + fun cancel(uploadId: String, requestOptions: RequestOptions): Upload = + cancel(uploadId, UploadCancelParams.none(), requestOptions) + /** * Completes the [Upload](https://platform.openai.com/docs/api-reference/uploads/object). * @@ -70,6 +88,17 @@ interface UploadService { * specified when creating the Upload object. No Parts may be added after an Upload is * completed. */ + fun complete(uploadId: String, params: UploadCompleteParams): Upload = + complete(uploadId, params, RequestOptions.none()) + + /** @see [complete] */ + fun complete( + uploadId: String, + params: UploadCompleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Upload = complete(params.toBuilder().uploadId(uploadId).build(), requestOptions) + + /** @see [complete] */ fun complete(params: UploadCompleteParams): Upload = complete(params, RequestOptions.none()) /** @see [complete] */ @@ -103,8 +132,24 @@ interface UploadService { * same as [UploadService.cancel]. */ @MustBeClosed - fun cancel(params: UploadCancelParams): HttpResponseFor = - cancel(params, RequestOptions.none()) + fun cancel(uploadId: String): HttpResponseFor = + cancel(uploadId, UploadCancelParams.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + uploadId: String, + params: UploadCancelParams = UploadCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + cancel(params.toBuilder().uploadId(uploadId).build(), requestOptions) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + uploadId: String, + params: UploadCancelParams = UploadCancelParams.none(), + ): HttpResponseFor = cancel(uploadId, params, RequestOptions.none()) /** @see [cancel] */ @MustBeClosed @@ -113,11 +158,35 @@ interface UploadService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [cancel] */ + @MustBeClosed + fun cancel(params: UploadCancelParams): HttpResponseFor = + cancel(params, RequestOptions.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel(uploadId: String, requestOptions: RequestOptions): HttpResponseFor = + cancel(uploadId, UploadCancelParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /uploads/{upload_id}/complete`, but is otherwise * the same as [UploadService.complete]. */ @MustBeClosed + fun complete(uploadId: String, params: UploadCompleteParams): HttpResponseFor = + complete(uploadId, params, RequestOptions.none()) + + /** @see [complete] */ + @MustBeClosed + fun complete( + uploadId: String, + params: UploadCompleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + complete(params.toBuilder().uploadId(uploadId).build(), requestOptions) + + /** @see [complete] */ + @MustBeClosed fun complete(params: UploadCompleteParams): HttpResponseFor = complete(params, RequestOptions.none()) 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 d2841644..6b2d7733 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 @@ -4,6 +4,7 @@ package com.openai.services.blocking import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -21,6 +22,7 @@ import com.openai.models.uploads.UploadCompleteParams import com.openai.models.uploads.UploadCreateParams import com.openai.services.blocking.uploads.PartService import com.openai.services.blocking.uploads.PartServiceImpl +import kotlin.jvm.optionals.getOrNull class UploadServiceImpl internal constructor(private val clientOptions: ClientOptions) : UploadService { @@ -92,6 +94,9 @@ class UploadServiceImpl internal constructor(private val clientOptions: ClientOp params: UploadCancelParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("uploadId", params.uploadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -119,6 +124,9 @@ class UploadServiceImpl internal constructor(private val clientOptions: ClientOp params: UploadCompleteParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("uploadId", params.uploadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/VectorStoreService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/VectorStoreService.kt index b61e9ce2..e5256a03 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/VectorStoreService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/VectorStoreService.kt @@ -47,8 +47,22 @@ interface VectorStoreService { create(VectorStoreCreateParams.none(), requestOptions) /** Retrieves a vector store. */ - fun retrieve(params: VectorStoreRetrieveParams): VectorStore = - retrieve(params, RequestOptions.none()) + fun retrieve(vectorStoreId: String): VectorStore = + retrieve(vectorStoreId, VectorStoreRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + vectorStoreId: String, + params: VectorStoreRetrieveParams = VectorStoreRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): VectorStore = + retrieve(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + vectorStoreId: String, + params: VectorStoreRetrieveParams = VectorStoreRetrieveParams.none(), + ): VectorStore = retrieve(vectorStoreId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -56,8 +70,30 @@ interface VectorStoreService { requestOptions: RequestOptions = RequestOptions.none(), ): VectorStore + /** @see [retrieve] */ + fun retrieve(params: VectorStoreRetrieveParams): VectorStore = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve(vectorStoreId: String, requestOptions: RequestOptions): VectorStore = + retrieve(vectorStoreId, VectorStoreRetrieveParams.none(), requestOptions) + /** Modifies a vector store. */ - fun update(params: VectorStoreUpdateParams): VectorStore = update(params, RequestOptions.none()) + fun update(vectorStoreId: String): VectorStore = + update(vectorStoreId, VectorStoreUpdateParams.none()) + + /** @see [update] */ + fun update( + vectorStoreId: String, + params: VectorStoreUpdateParams = VectorStoreUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): VectorStore = update(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [update] */ + fun update( + vectorStoreId: String, + params: VectorStoreUpdateParams = VectorStoreUpdateParams.none(), + ): VectorStore = update(vectorStoreId, params, RequestOptions.none()) /** @see [update] */ fun update( @@ -65,6 +101,13 @@ interface VectorStoreService { requestOptions: RequestOptions = RequestOptions.none(), ): VectorStore + /** @see [update] */ + fun update(params: VectorStoreUpdateParams): VectorStore = update(params, RequestOptions.none()) + + /** @see [update] */ + fun update(vectorStoreId: String, requestOptions: RequestOptions): VectorStore = + update(vectorStoreId, VectorStoreUpdateParams.none(), requestOptions) + /** Returns a list of vector stores. */ fun list(): VectorStoreListPage = list(VectorStoreListParams.none()) @@ -83,8 +126,22 @@ interface VectorStoreService { list(VectorStoreListParams.none(), requestOptions) /** Delete a vector store. */ - fun delete(params: VectorStoreDeleteParams): VectorStoreDeleted = - delete(params, RequestOptions.none()) + fun delete(vectorStoreId: String): VectorStoreDeleted = + delete(vectorStoreId, VectorStoreDeleteParams.none()) + + /** @see [delete] */ + fun delete( + vectorStoreId: String, + params: VectorStoreDeleteParams = VectorStoreDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): VectorStoreDeleted = + delete(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [delete] */ + fun delete( + vectorStoreId: String, + params: VectorStoreDeleteParams = VectorStoreDeleteParams.none(), + ): VectorStoreDeleted = delete(vectorStoreId, params, RequestOptions.none()) /** @see [delete] */ fun delete( @@ -92,7 +149,27 @@ interface VectorStoreService { requestOptions: RequestOptions = RequestOptions.none(), ): VectorStoreDeleted + /** @see [delete] */ + fun delete(params: VectorStoreDeleteParams): VectorStoreDeleted = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete(vectorStoreId: String, requestOptions: RequestOptions): VectorStoreDeleted = + delete(vectorStoreId, VectorStoreDeleteParams.none(), requestOptions) + /** Search a vector store for relevant chunks based on a query and file attributes filter. */ + fun search(vectorStoreId: String, params: VectorStoreSearchParams): VectorStoreSearchPage = + search(vectorStoreId, params, RequestOptions.none()) + + /** @see [search] */ + fun search( + vectorStoreId: String, + params: VectorStoreSearchParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): VectorStoreSearchPage = + search(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [search] */ fun search(params: VectorStoreSearchParams): VectorStoreSearchPage = search(params, RequestOptions.none()) @@ -141,8 +218,24 @@ interface VectorStoreService { * the same as [VectorStoreService.retrieve]. */ @MustBeClosed - fun retrieve(params: VectorStoreRetrieveParams): HttpResponseFor = - retrieve(params, RequestOptions.none()) + fun retrieve(vectorStoreId: String): HttpResponseFor = + retrieve(vectorStoreId, VectorStoreRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + vectorStoreId: String, + params: VectorStoreRetrieveParams = VectorStoreRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + vectorStoreId: String, + params: VectorStoreRetrieveParams = VectorStoreRetrieveParams.none(), + ): HttpResponseFor = retrieve(vectorStoreId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -151,13 +244,42 @@ interface VectorStoreService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: VectorStoreRetrieveParams): HttpResponseFor = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + vectorStoreId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + retrieve(vectorStoreId, VectorStoreRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /vector_stores/{vector_store_id}`, but is otherwise * the same as [VectorStoreService.update]. */ @MustBeClosed - fun update(params: VectorStoreUpdateParams): HttpResponseFor = - update(params, RequestOptions.none()) + fun update(vectorStoreId: String): HttpResponseFor = + update(vectorStoreId, VectorStoreUpdateParams.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + vectorStoreId: String, + params: VectorStoreUpdateParams = VectorStoreUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + update(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed + fun update( + vectorStoreId: String, + params: VectorStoreUpdateParams = VectorStoreUpdateParams.none(), + ): HttpResponseFor = update(vectorStoreId, params, RequestOptions.none()) /** @see [update] */ @MustBeClosed @@ -166,6 +288,19 @@ interface VectorStoreService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [update] */ + @MustBeClosed + fun update(params: VectorStoreUpdateParams): HttpResponseFor = + update(params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + vectorStoreId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + update(vectorStoreId, VectorStoreUpdateParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /vector_stores`, but is otherwise the same as * [VectorStoreService.list]. @@ -196,8 +331,25 @@ interface VectorStoreService { * otherwise the same as [VectorStoreService.delete]. */ @MustBeClosed - fun delete(params: VectorStoreDeleteParams): HttpResponseFor = - delete(params, RequestOptions.none()) + fun delete(vectorStoreId: String): HttpResponseFor = + delete(vectorStoreId, VectorStoreDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + vectorStoreId: String, + params: VectorStoreDeleteParams = VectorStoreDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + delete(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + vectorStoreId: String, + params: VectorStoreDeleteParams = VectorStoreDeleteParams.none(), + ): HttpResponseFor = + delete(vectorStoreId, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -206,11 +358,41 @@ interface VectorStoreService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [delete] */ + @MustBeClosed + fun delete(params: VectorStoreDeleteParams): HttpResponseFor = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + vectorStoreId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + delete(vectorStoreId, VectorStoreDeleteParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /vector_stores/{vector_store_id}/search`, but is * otherwise the same as [VectorStoreService.search]. */ @MustBeClosed + fun search( + vectorStoreId: String, + params: VectorStoreSearchParams, + ): HttpResponseFor = + search(vectorStoreId, params, RequestOptions.none()) + + /** @see [search] */ + @MustBeClosed + fun search( + vectorStoreId: String, + params: VectorStoreSearchParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + search(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [search] */ + @MustBeClosed fun search(params: VectorStoreSearchParams): HttpResponseFor = search(params, RequestOptions.none()) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/VectorStoreServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/VectorStoreServiceImpl.kt index 5a077091..89327679 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/VectorStoreServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/VectorStoreServiceImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.blocking import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -32,6 +33,7 @@ import com.openai.services.blocking.vectorstores.FileBatchService import com.openai.services.blocking.vectorstores.FileBatchServiceImpl import com.openai.services.blocking.vectorstores.FileService import com.openai.services.blocking.vectorstores.FileServiceImpl +import kotlin.jvm.optionals.getOrNull class VectorStoreServiceImpl internal constructor(private val clientOptions: ClientOptions) : VectorStoreService { @@ -149,6 +151,9 @@ class VectorStoreServiceImpl internal constructor(private val clientOptions: Cli params: VectorStoreRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("vectorStoreId", params.vectorStoreId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -176,6 +181,9 @@ class VectorStoreServiceImpl internal constructor(private val clientOptions: Cli params: VectorStoreUpdateParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("vectorStoreId", params.vectorStoreId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -239,6 +247,9 @@ class VectorStoreServiceImpl internal constructor(private val clientOptions: Cli params: VectorStoreDeleteParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("vectorStoreId", params.vectorStoreId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) @@ -268,6 +279,9 @@ class VectorStoreServiceImpl internal constructor(private val clientOptions: Cli params: VectorStoreSearchParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("vectorStoreId", params.vectorStoreId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) 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 2943bace..98d5e195 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 @@ -31,8 +31,21 @@ interface AssistantService { ): Assistant /** Retrieves an assistant. */ - fun retrieve(params: AssistantRetrieveParams): Assistant = - retrieve(params, RequestOptions.none()) + fun retrieve(assistantId: String): Assistant = + retrieve(assistantId, AssistantRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + assistantId: String, + params: AssistantRetrieveParams = AssistantRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): Assistant = retrieve(params.toBuilder().assistantId(assistantId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + assistantId: String, + params: AssistantRetrieveParams = AssistantRetrieveParams.none(), + ): Assistant = retrieve(assistantId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -40,8 +53,29 @@ interface AssistantService { requestOptions: RequestOptions = RequestOptions.none(), ): Assistant + /** @see [retrieve] */ + fun retrieve(params: AssistantRetrieveParams): Assistant = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve(assistantId: String, requestOptions: RequestOptions): Assistant = + retrieve(assistantId, AssistantRetrieveParams.none(), requestOptions) + /** Modifies an assistant. */ - fun update(params: AssistantUpdateParams): Assistant = update(params, RequestOptions.none()) + fun update(assistantId: String): Assistant = update(assistantId, AssistantUpdateParams.none()) + + /** @see [update] */ + fun update( + assistantId: String, + params: AssistantUpdateParams = AssistantUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): Assistant = update(params.toBuilder().assistantId(assistantId).build(), requestOptions) + + /** @see [update] */ + fun update( + assistantId: String, + params: AssistantUpdateParams = AssistantUpdateParams.none(), + ): Assistant = update(assistantId, params, RequestOptions.none()) /** @see [update] */ fun update( @@ -49,6 +83,13 @@ interface AssistantService { requestOptions: RequestOptions = RequestOptions.none(), ): Assistant + /** @see [update] */ + fun update(params: AssistantUpdateParams): Assistant = update(params, RequestOptions.none()) + + /** @see [update] */ + fun update(assistantId: String, requestOptions: RequestOptions): Assistant = + update(assistantId, AssistantUpdateParams.none(), requestOptions) + /** Returns a list of assistants. */ fun list(): AssistantListPage = list(AssistantListParams.none()) @@ -67,8 +108,22 @@ interface AssistantService { list(AssistantListParams.none(), requestOptions) /** Delete an assistant. */ - fun delete(params: AssistantDeleteParams): AssistantDeleted = - delete(params, RequestOptions.none()) + fun delete(assistantId: String): AssistantDeleted = + delete(assistantId, AssistantDeleteParams.none()) + + /** @see [delete] */ + fun delete( + assistantId: String, + params: AssistantDeleteParams = AssistantDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): AssistantDeleted = + delete(params.toBuilder().assistantId(assistantId).build(), requestOptions) + + /** @see [delete] */ + fun delete( + assistantId: String, + params: AssistantDeleteParams = AssistantDeleteParams.none(), + ): AssistantDeleted = delete(assistantId, params, RequestOptions.none()) /** @see [delete] */ fun delete( @@ -76,6 +131,14 @@ interface AssistantService { requestOptions: RequestOptions = RequestOptions.none(), ): AssistantDeleted + /** @see [delete] */ + fun delete(params: AssistantDeleteParams): AssistantDeleted = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete(assistantId: String, requestOptions: RequestOptions): AssistantDeleted = + delete(assistantId, AssistantDeleteParams.none(), requestOptions) + /** A view of [AssistantService] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -99,8 +162,24 @@ interface AssistantService { * same as [AssistantService.retrieve]. */ @MustBeClosed - fun retrieve(params: AssistantRetrieveParams): HttpResponseFor = - retrieve(params, RequestOptions.none()) + fun retrieve(assistantId: String): HttpResponseFor = + retrieve(assistantId, AssistantRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + assistantId: String, + params: AssistantRetrieveParams = AssistantRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().assistantId(assistantId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + assistantId: String, + params: AssistantRetrieveParams = AssistantRetrieveParams.none(), + ): HttpResponseFor = retrieve(assistantId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -109,13 +188,42 @@ interface AssistantService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: AssistantRetrieveParams): HttpResponseFor = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + assistantId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + retrieve(assistantId, AssistantRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /assistants/{assistant_id}`, but is otherwise the * same as [AssistantService.update]. */ @MustBeClosed - fun update(params: AssistantUpdateParams): HttpResponseFor = - update(params, RequestOptions.none()) + fun update(assistantId: String): HttpResponseFor = + update(assistantId, AssistantUpdateParams.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + assistantId: String, + params: AssistantUpdateParams = AssistantUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + update(params.toBuilder().assistantId(assistantId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed + fun update( + assistantId: String, + params: AssistantUpdateParams = AssistantUpdateParams.none(), + ): HttpResponseFor = update(assistantId, params, RequestOptions.none()) /** @see [update] */ @MustBeClosed @@ -124,6 +232,19 @@ interface AssistantService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [update] */ + @MustBeClosed + fun update(params: AssistantUpdateParams): HttpResponseFor = + update(params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + assistantId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + update(assistantId, AssistantUpdateParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /assistants`, but is otherwise the same as * [AssistantService.list]. @@ -154,8 +275,24 @@ interface AssistantService { * same as [AssistantService.delete]. */ @MustBeClosed - fun delete(params: AssistantDeleteParams): HttpResponseFor = - delete(params, RequestOptions.none()) + fun delete(assistantId: String): HttpResponseFor = + delete(assistantId, AssistantDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + assistantId: String, + params: AssistantDeleteParams = AssistantDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + delete(params.toBuilder().assistantId(assistantId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + assistantId: String, + params: AssistantDeleteParams = AssistantDeleteParams.none(), + ): HttpResponseFor = delete(assistantId, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -163,5 +300,18 @@ interface AssistantService { params: AssistantDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + + /** @see [delete] */ + @MustBeClosed + fun delete(params: AssistantDeleteParams): HttpResponseFor = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + assistantId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + delete(assistantId, AssistantDeleteParams.none(), requestOptions) } } 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 521543aa..30ca6d1b 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 @@ -4,6 +4,7 @@ package com.openai.services.blocking.beta import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -25,6 +26,7 @@ import com.openai.models.beta.assistants.AssistantListPageResponse import com.openai.models.beta.assistants.AssistantListParams import com.openai.models.beta.assistants.AssistantRetrieveParams import com.openai.models.beta.assistants.AssistantUpdateParams +import kotlin.jvm.optionals.getOrNull class AssistantServiceImpl internal constructor(private val clientOptions: ClientOptions) : AssistantService { @@ -109,6 +111,9 @@ class AssistantServiceImpl internal constructor(private val clientOptions: Clien params: AssistantRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("assistantId", params.assistantId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -136,6 +141,9 @@ class AssistantServiceImpl internal constructor(private val clientOptions: Clien params: AssistantUpdateParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("assistantId", params.assistantId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -203,6 +211,9 @@ class AssistantServiceImpl internal constructor(private val clientOptions: Clien params: AssistantDeleteParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("assistantId", params.assistantId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) 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 8263aa93..a3626111 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 @@ -47,7 +47,20 @@ interface ThreadService { create(ThreadCreateParams.none(), requestOptions) /** Retrieves a thread. */ - fun retrieve(params: ThreadRetrieveParams): Thread = retrieve(params, RequestOptions.none()) + fun retrieve(threadId: String): Thread = retrieve(threadId, ThreadRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + threadId: String, + params: ThreadRetrieveParams = ThreadRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): Thread = retrieve(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + threadId: String, + params: ThreadRetrieveParams = ThreadRetrieveParams.none(), + ): Thread = retrieve(threadId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -55,8 +68,26 @@ interface ThreadService { requestOptions: RequestOptions = RequestOptions.none(), ): Thread + /** @see [retrieve] */ + fun retrieve(params: ThreadRetrieveParams): Thread = retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve(threadId: String, requestOptions: RequestOptions): Thread = + retrieve(threadId, ThreadRetrieveParams.none(), requestOptions) + /** Modifies a thread. */ - fun update(params: ThreadUpdateParams): Thread = update(params, RequestOptions.none()) + fun update(threadId: String): Thread = update(threadId, ThreadUpdateParams.none()) + + /** @see [update] */ + fun update( + threadId: String, + params: ThreadUpdateParams = ThreadUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): Thread = update(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [update] */ + fun update(threadId: String, params: ThreadUpdateParams = ThreadUpdateParams.none()): Thread = + update(threadId, params, RequestOptions.none()) /** @see [update] */ fun update( @@ -64,8 +95,28 @@ interface ThreadService { requestOptions: RequestOptions = RequestOptions.none(), ): Thread + /** @see [update] */ + fun update(params: ThreadUpdateParams): Thread = update(params, RequestOptions.none()) + + /** @see [update] */ + fun update(threadId: String, requestOptions: RequestOptions): Thread = + update(threadId, ThreadUpdateParams.none(), requestOptions) + /** Delete a thread. */ - fun delete(params: ThreadDeleteParams): ThreadDeleted = delete(params, RequestOptions.none()) + fun delete(threadId: String): ThreadDeleted = delete(threadId, ThreadDeleteParams.none()) + + /** @see [delete] */ + fun delete( + threadId: String, + params: ThreadDeleteParams = ThreadDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ThreadDeleted = delete(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [delete] */ + fun delete( + threadId: String, + params: ThreadDeleteParams = ThreadDeleteParams.none(), + ): ThreadDeleted = delete(threadId, params, RequestOptions.none()) /** @see [delete] */ fun delete( @@ -73,6 +124,13 @@ interface ThreadService { requestOptions: RequestOptions = RequestOptions.none(), ): ThreadDeleted + /** @see [delete] */ + fun delete(params: ThreadDeleteParams): ThreadDeleted = delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete(threadId: String, requestOptions: RequestOptions): ThreadDeleted = + delete(threadId, ThreadDeleteParams.none(), requestOptions) + /** Create a thread and run it in one request. */ fun createAndRun(params: ThreadCreateAndRunParams): Run = createAndRun(params, RequestOptions.none()) @@ -132,8 +190,24 @@ interface ThreadService { * [ThreadService.retrieve]. */ @MustBeClosed - fun retrieve(params: ThreadRetrieveParams): HttpResponseFor = - retrieve(params, RequestOptions.none()) + fun retrieve(threadId: String): HttpResponseFor = + retrieve(threadId, ThreadRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + threadId: String, + params: ThreadRetrieveParams = ThreadRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + threadId: String, + params: ThreadRetrieveParams = ThreadRetrieveParams.none(), + ): HttpResponseFor = retrieve(threadId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -142,13 +216,39 @@ interface ThreadService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: ThreadRetrieveParams): HttpResponseFor = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(threadId: String, requestOptions: RequestOptions): HttpResponseFor = + retrieve(threadId, ThreadRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /threads/{thread_id}`, but is otherwise the same as * [ThreadService.update]. */ @MustBeClosed - fun update(params: ThreadUpdateParams): HttpResponseFor = - update(params, RequestOptions.none()) + fun update(threadId: String): HttpResponseFor = + update(threadId, ThreadUpdateParams.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + threadId: String, + params: ThreadUpdateParams = ThreadUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + update(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed + fun update( + threadId: String, + params: ThreadUpdateParams = ThreadUpdateParams.none(), + ): HttpResponseFor = update(threadId, params, RequestOptions.none()) /** @see [update] */ @MustBeClosed @@ -157,13 +257,39 @@ interface ThreadService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [update] */ + @MustBeClosed + fun update(params: ThreadUpdateParams): HttpResponseFor = + update(params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update(threadId: String, requestOptions: RequestOptions): HttpResponseFor = + update(threadId, ThreadUpdateParams.none(), requestOptions) + /** * Returns a raw HTTP response for `delete /threads/{thread_id}`, but is otherwise the same * as [ThreadService.delete]. */ @MustBeClosed - fun delete(params: ThreadDeleteParams): HttpResponseFor = - delete(params, RequestOptions.none()) + fun delete(threadId: String): HttpResponseFor = + delete(threadId, ThreadDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + threadId: String, + params: ThreadDeleteParams = ThreadDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + delete(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + threadId: String, + params: ThreadDeleteParams = ThreadDeleteParams.none(), + ): HttpResponseFor = delete(threadId, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -172,6 +298,19 @@ interface ThreadService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [delete] */ + @MustBeClosed + fun delete(params: ThreadDeleteParams): HttpResponseFor = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + threadId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + delete(threadId, ThreadDeleteParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /threads/runs`, but is otherwise the same as * [ThreadService.createAndRun]. 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 5d7ec922..fa03e9bd 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 @@ -5,6 +5,7 @@ package com.openai.services.blocking.beta import com.openai.core.ClientOptions import com.openai.core.JsonValue import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.mapJson @@ -34,6 +35,7 @@ import com.openai.services.blocking.beta.threads.MessageService import com.openai.services.blocking.beta.threads.MessageServiceImpl import com.openai.services.blocking.beta.threads.RunService import com.openai.services.blocking.beta.threads.RunServiceImpl +import kotlin.jvm.optionals.getOrNull class ThreadServiceImpl internal constructor(private val clientOptions: ClientOptions) : ThreadService { @@ -139,6 +141,9 @@ class ThreadServiceImpl internal constructor(private val clientOptions: ClientOp params: ThreadRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -166,6 +171,9 @@ class ThreadServiceImpl internal constructor(private val clientOptions: ClientOp params: ThreadUpdateParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -194,6 +202,9 @@ class ThreadServiceImpl internal constructor(private val clientOptions: ClientOp params: ThreadDeleteParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) 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 87184f95..86e7ea88 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 @@ -22,6 +22,17 @@ interface MessageService { fun withRawResponse(): WithRawResponse /** Create a message. */ + fun create(threadId: String, params: MessageCreateParams): Message = + create(threadId, params, RequestOptions.none()) + + /** @see [create] */ + fun create( + threadId: String, + params: MessageCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Message = create(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [create] */ fun create(params: MessageCreateParams): Message = create(params, RequestOptions.none()) /** @see [create] */ @@ -31,6 +42,17 @@ interface MessageService { ): Message /** Retrieve a message. */ + fun retrieve(messageId: String, params: MessageRetrieveParams): Message = + retrieve(messageId, params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + messageId: String, + params: MessageRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Message = retrieve(params.toBuilder().messageId(messageId).build(), requestOptions) + + /** @see [retrieve] */ fun retrieve(params: MessageRetrieveParams): Message = retrieve(params, RequestOptions.none()) /** @see [retrieve] */ @@ -40,6 +62,17 @@ interface MessageService { ): Message /** Modifies a message. */ + fun update(messageId: String, params: MessageUpdateParams): Message = + update(messageId, params, RequestOptions.none()) + + /** @see [update] */ + fun update( + messageId: String, + params: MessageUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Message = update(params.toBuilder().messageId(messageId).build(), requestOptions) + + /** @see [update] */ fun update(params: MessageUpdateParams): Message = update(params, RequestOptions.none()) /** @see [update] */ @@ -49,7 +82,20 @@ interface MessageService { ): Message /** Returns a list of messages for a given thread. */ - fun list(params: MessageListParams): MessageListPage = list(params, RequestOptions.none()) + fun list(threadId: String): MessageListPage = list(threadId, MessageListParams.none()) + + /** @see [list] */ + fun list( + threadId: String, + params: MessageListParams = MessageListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): MessageListPage = list(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [list] */ + fun list( + threadId: String, + params: MessageListParams = MessageListParams.none(), + ): MessageListPage = list(threadId, params, RequestOptions.none()) /** @see [list] */ fun list( @@ -57,7 +103,25 @@ interface MessageService { requestOptions: RequestOptions = RequestOptions.none(), ): MessageListPage + /** @see [list] */ + fun list(params: MessageListParams): MessageListPage = list(params, RequestOptions.none()) + + /** @see [list] */ + fun list(threadId: String, requestOptions: RequestOptions): MessageListPage = + list(threadId, MessageListParams.none(), requestOptions) + /** Deletes a message. */ + fun delete(messageId: String, params: MessageDeleteParams): MessageDeleted = + delete(messageId, params, RequestOptions.none()) + + /** @see [delete] */ + fun delete( + messageId: String, + params: MessageDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): MessageDeleted = delete(params.toBuilder().messageId(messageId).build(), requestOptions) + + /** @see [delete] */ fun delete(params: MessageDeleteParams): MessageDeleted = delete(params, RequestOptions.none()) /** @see [delete] */ @@ -74,6 +138,20 @@ interface MessageService { * the same as [MessageService.create]. */ @MustBeClosed + fun create(threadId: String, params: MessageCreateParams): HttpResponseFor = + create(threadId, params, RequestOptions.none()) + + /** @see [create] */ + @MustBeClosed + fun create( + threadId: String, + params: MessageCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + create(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [create] */ + @MustBeClosed fun create(params: MessageCreateParams): HttpResponseFor = create(params, RequestOptions.none()) @@ -89,6 +167,20 @@ interface MessageService { * otherwise the same as [MessageService.retrieve]. */ @MustBeClosed + fun retrieve(messageId: String, params: MessageRetrieveParams): HttpResponseFor = + retrieve(messageId, params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + messageId: String, + params: MessageRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().messageId(messageId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve(params: MessageRetrieveParams): HttpResponseFor = retrieve(params, RequestOptions.none()) @@ -104,6 +196,20 @@ interface MessageService { * otherwise the same as [MessageService.update]. */ @MustBeClosed + fun update(messageId: String, params: MessageUpdateParams): HttpResponseFor = + update(messageId, params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + messageId: String, + params: MessageUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + update(params.toBuilder().messageId(messageId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed fun update(params: MessageUpdateParams): HttpResponseFor = update(params, RequestOptions.none()) @@ -119,8 +225,24 @@ interface MessageService { * same as [MessageService.list]. */ @MustBeClosed - fun list(params: MessageListParams): HttpResponseFor = - list(params, RequestOptions.none()) + fun list(threadId: String): HttpResponseFor = + list(threadId, MessageListParams.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + threadId: String, + params: MessageListParams = MessageListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + list(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed + fun list( + threadId: String, + params: MessageListParams = MessageListParams.none(), + ): HttpResponseFor = list(threadId, params, RequestOptions.none()) /** @see [list] */ @MustBeClosed @@ -129,11 +251,40 @@ interface MessageService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [list] */ + @MustBeClosed + fun list(params: MessageListParams): HttpResponseFor = + list(params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + threadId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + list(threadId, MessageListParams.none(), requestOptions) + /** * Returns a raw HTTP response for `delete /threads/{thread_id}/messages/{message_id}`, but * is otherwise the same as [MessageService.delete]. */ @MustBeClosed + fun delete( + messageId: String, + params: MessageDeleteParams, + ): HttpResponseFor = delete(messageId, params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + messageId: String, + params: MessageDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + delete(params.toBuilder().messageId(messageId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed fun delete(params: MessageDeleteParams): HttpResponseFor = delete(params, RequestOptions.none()) 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 fddd1451..83e8bc6c 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 @@ -4,6 +4,7 @@ package com.openai.services.blocking.beta.threads import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -25,6 +26,7 @@ import com.openai.models.beta.threads.messages.MessageListPageResponse import com.openai.models.beta.threads.messages.MessageListParams import com.openai.models.beta.threads.messages.MessageRetrieveParams import com.openai.models.beta.threads.messages.MessageUpdateParams +import kotlin.jvm.optionals.getOrNull class MessageServiceImpl internal constructor(private val clientOptions: ClientOptions) : MessageService { @@ -75,6 +77,9 @@ class MessageServiceImpl internal constructor(private val clientOptions: ClientO params: MessageCreateParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -103,6 +108,9 @@ class MessageServiceImpl internal constructor(private val clientOptions: ClientO params: MessageRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("messageId", params.messageId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -135,6 +143,9 @@ class MessageServiceImpl internal constructor(private val clientOptions: ClientO params: MessageUpdateParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("messageId", params.messageId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -169,6 +180,9 @@ class MessageServiceImpl internal constructor(private val clientOptions: ClientO params: MessageListParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -203,6 +217,9 @@ class MessageServiceImpl internal constructor(private val clientOptions: ClientO params: MessageDeleteParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("messageId", params.messageId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) 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 02562d7e..0708353b 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 @@ -27,6 +27,17 @@ interface RunService { fun steps(): StepService /** Create a run. */ + fun create(threadId: String, params: RunCreateParams): Run = + create(threadId, params, RequestOptions.none()) + + /** @see [create] */ + fun create( + threadId: String, + params: RunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Run = create(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [create] */ fun create(params: RunCreateParams): Run = create(params, RequestOptions.none()) /** @see [create] */ @@ -34,6 +45,23 @@ interface RunService { /** Create a run. */ @MustBeClosed + fun createStreaming( + threadId: String, + params: RunCreateParams, + ): StreamResponse = + createStreaming(threadId, params, RequestOptions.none()) + + /** @see [createStreaming] */ + @MustBeClosed + fun createStreaming( + threadId: String, + params: RunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): StreamResponse = + createStreaming(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [createStreaming] */ + @MustBeClosed fun createStreaming(params: RunCreateParams): StreamResponse = createStreaming(params, RequestOptions.none()) @@ -45,6 +73,17 @@ interface RunService { ): StreamResponse /** Retrieves a run. */ + fun retrieve(runId: String, params: RunRetrieveParams): Run = + retrieve(runId, params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + runId: String, + params: RunRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Run = retrieve(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [retrieve] */ fun retrieve(params: RunRetrieveParams): Run = retrieve(params, RequestOptions.none()) /** @see [retrieve] */ @@ -54,13 +93,35 @@ interface RunService { ): Run /** Modifies a run. */ + fun update(runId: String, params: RunUpdateParams): Run = + update(runId, params, RequestOptions.none()) + + /** @see [update] */ + fun update( + runId: String, + params: RunUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Run = update(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [update] */ fun update(params: RunUpdateParams): Run = update(params, RequestOptions.none()) /** @see [update] */ fun update(params: RunUpdateParams, requestOptions: RequestOptions = RequestOptions.none()): Run /** Returns a list of runs belonging to a thread. */ - fun list(params: RunListParams): RunListPage = list(params, RequestOptions.none()) + fun list(threadId: String): RunListPage = list(threadId, RunListParams.none()) + + /** @see [list] */ + fun list( + threadId: String, + params: RunListParams = RunListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): RunListPage = list(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [list] */ + fun list(threadId: String, params: RunListParams = RunListParams.none()): RunListPage = + list(threadId, params, RequestOptions.none()) /** @see [list] */ fun list( @@ -68,7 +129,25 @@ interface RunService { requestOptions: RequestOptions = RequestOptions.none(), ): RunListPage + /** @see [list] */ + fun list(params: RunListParams): RunListPage = list(params, RequestOptions.none()) + + /** @see [list] */ + fun list(threadId: String, requestOptions: RequestOptions): RunListPage = + list(threadId, RunListParams.none(), requestOptions) + /** Cancels a run that is `in_progress`. */ + fun cancel(runId: String, params: RunCancelParams): Run = + cancel(runId, params, RequestOptions.none()) + + /** @see [cancel] */ + fun cancel( + runId: String, + params: RunCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Run = cancel(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [cancel] */ fun cancel(params: RunCancelParams): Run = cancel(params, RequestOptions.none()) /** @see [cancel] */ @@ -79,6 +158,17 @@ interface RunService { * `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. */ + fun submitToolOutputs(runId: String, params: RunSubmitToolOutputsParams): Run = + submitToolOutputs(runId, params, RequestOptions.none()) + + /** @see [submitToolOutputs] */ + fun submitToolOutputs( + runId: String, + params: RunSubmitToolOutputsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Run = submitToolOutputs(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [submitToolOutputs] */ fun submitToolOutputs(params: RunSubmitToolOutputsParams): Run = submitToolOutputs(params, RequestOptions.none()) @@ -94,6 +184,23 @@ interface RunService { * once they're all completed. All outputs must be submitted in a single request. */ @MustBeClosed + fun submitToolOutputsStreaming( + runId: String, + params: RunSubmitToolOutputsParams, + ): StreamResponse = + submitToolOutputsStreaming(runId, params, RequestOptions.none()) + + /** @see [submitToolOutputsStreaming] */ + @MustBeClosed + fun submitToolOutputsStreaming( + runId: String, + params: RunSubmitToolOutputsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): StreamResponse = + submitToolOutputsStreaming(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [submitToolOutputsStreaming] */ + @MustBeClosed fun submitToolOutputsStreaming( params: RunSubmitToolOutputsParams ): StreamResponse = @@ -116,6 +223,20 @@ interface RunService { * same as [RunService.create]. */ @MustBeClosed + fun create(threadId: String, params: RunCreateParams): HttpResponseFor = + create(threadId, params, RequestOptions.none()) + + /** @see [create] */ + @MustBeClosed + fun create( + threadId: String, + params: RunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + create(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [create] */ + @MustBeClosed fun create(params: RunCreateParams): HttpResponseFor = create(params, RequestOptions.none()) @@ -131,6 +252,23 @@ interface RunService { * same as [RunService.createStreaming]. */ @MustBeClosed + fun createStreaming( + threadId: String, + params: RunCreateParams, + ): HttpResponseFor> = + createStreaming(threadId, params, RequestOptions.none()) + + /** @see [createStreaming] */ + @MustBeClosed + fun createStreaming( + threadId: String, + params: RunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> = + createStreaming(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [createStreaming] */ + @MustBeClosed fun createStreaming( params: RunCreateParams ): HttpResponseFor> = @@ -148,6 +286,19 @@ interface RunService { * otherwise the same as [RunService.retrieve]. */ @MustBeClosed + fun retrieve(runId: String, params: RunRetrieveParams): HttpResponseFor = + retrieve(runId, params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + runId: String, + params: RunRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = retrieve(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve(params: RunRetrieveParams): HttpResponseFor = retrieve(params, RequestOptions.none()) @@ -163,6 +314,19 @@ interface RunService { * otherwise the same as [RunService.update]. */ @MustBeClosed + fun update(runId: String, params: RunUpdateParams): HttpResponseFor = + update(runId, params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + runId: String, + params: RunUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = update(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed fun update(params: RunUpdateParams): HttpResponseFor = update(params, RequestOptions.none()) @@ -178,8 +342,24 @@ interface RunService { * same as [RunService.list]. */ @MustBeClosed - fun list(params: RunListParams): HttpResponseFor = - list(params, RequestOptions.none()) + fun list(threadId: String): HttpResponseFor = + list(threadId, RunListParams.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + threadId: String, + params: RunListParams = RunListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + list(params.toBuilder().threadId(threadId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed + fun list( + threadId: String, + params: RunListParams = RunListParams.none(), + ): HttpResponseFor = list(threadId, params, RequestOptions.none()) /** @see [list] */ @MustBeClosed @@ -188,11 +368,34 @@ interface RunService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [list] */ + @MustBeClosed + fun list(params: RunListParams): HttpResponseFor = + list(params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list(threadId: String, requestOptions: RequestOptions): HttpResponseFor = + list(threadId, RunListParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /threads/{thread_id}/runs/{run_id}/cancel`, but is * otherwise the same as [RunService.cancel]. */ @MustBeClosed + fun cancel(runId: String, params: RunCancelParams): HttpResponseFor = + cancel(runId, params, RequestOptions.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + runId: String, + params: RunCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = cancel(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [cancel] */ + @MustBeClosed fun cancel(params: RunCancelParams): HttpResponseFor = cancel(params, RequestOptions.none()) @@ -209,6 +412,22 @@ interface RunService { * [RunService.submitToolOutputs]. */ @MustBeClosed + fun submitToolOutputs( + runId: String, + params: RunSubmitToolOutputsParams, + ): HttpResponseFor = submitToolOutputs(runId, params, RequestOptions.none()) + + /** @see [submitToolOutputs] */ + @MustBeClosed + fun submitToolOutputs( + runId: String, + params: RunSubmitToolOutputsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + submitToolOutputs(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [submitToolOutputs] */ + @MustBeClosed fun submitToolOutputs(params: RunSubmitToolOutputsParams): HttpResponseFor = submitToolOutputs(params, RequestOptions.none()) @@ -225,6 +444,23 @@ interface RunService { * [RunService.submitToolOutputsStreaming]. */ @MustBeClosed + fun submitToolOutputsStreaming( + runId: String, + params: RunSubmitToolOutputsParams, + ): HttpResponseFor> = + submitToolOutputsStreaming(runId, params, RequestOptions.none()) + + /** @see [submitToolOutputsStreaming] */ + @MustBeClosed + fun submitToolOutputsStreaming( + runId: String, + params: RunSubmitToolOutputsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> = + submitToolOutputsStreaming(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [submitToolOutputsStreaming] */ + @MustBeClosed fun submitToolOutputsStreaming( params: RunSubmitToolOutputsParams ): 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 0d7f999d..0f7f56e8 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 @@ -5,6 +5,7 @@ package com.openai.services.blocking.beta.threads import com.openai.core.ClientOptions import com.openai.core.JsonValue import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.mapJson @@ -33,6 +34,7 @@ import com.openai.models.beta.threads.runs.RunSubmitToolOutputsParams import com.openai.models.beta.threads.runs.RunUpdateParams import com.openai.services.blocking.beta.threads.runs.StepService import com.openai.services.blocking.beta.threads.runs.StepServiceImpl +import kotlin.jvm.optionals.getOrNull class RunServiceImpl internal constructor(private val clientOptions: ClientOptions) : RunService { @@ -110,6 +112,9 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio params: RunCreateParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -144,6 +149,9 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio params: RunCreateParams, requestOptions: RequestOptions, ): HttpResponseFor> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -187,6 +195,9 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio params: RunRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -214,6 +225,9 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio params: RunUpdateParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -243,6 +257,9 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio params: RunListParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("threadId", params.threadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -277,6 +294,9 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio params: RunCancelParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -311,6 +331,9 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio params: RunSubmitToolOutputsParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -348,6 +371,9 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio params: RunSubmitToolOutputsParams, requestOptions: RequestOptions, ): HttpResponseFor> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) 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 5b4bf471..6e5270de 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 @@ -18,6 +18,17 @@ interface StepService { fun withRawResponse(): WithRawResponse /** Retrieves a run step. */ + fun retrieve(stepId: String, params: StepRetrieveParams): RunStep = + retrieve(stepId, params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + stepId: String, + params: StepRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): RunStep = retrieve(params.toBuilder().stepId(stepId).build(), requestOptions) + + /** @see [retrieve] */ fun retrieve(params: StepRetrieveParams): RunStep = retrieve(params, RequestOptions.none()) /** @see [retrieve] */ @@ -27,6 +38,17 @@ interface StepService { ): RunStep /** Returns a list of run steps belonging to a run. */ + fun list(runId: String, params: StepListParams): StepListPage = + list(runId, params, RequestOptions.none()) + + /** @see [list] */ + fun list( + runId: String, + params: StepListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): StepListPage = list(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [list] */ fun list(params: StepListParams): StepListPage = list(params, RequestOptions.none()) /** @see [list] */ @@ -43,6 +65,20 @@ interface StepService { * but is otherwise the same as [StepService.retrieve]. */ @MustBeClosed + fun retrieve(stepId: String, params: StepRetrieveParams): HttpResponseFor = + retrieve(stepId, params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + stepId: String, + params: StepRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().stepId(stepId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve(params: StepRetrieveParams): HttpResponseFor = retrieve(params, RequestOptions.none()) @@ -58,6 +94,20 @@ interface StepService { * otherwise the same as [StepService.list]. */ @MustBeClosed + fun list(runId: String, params: StepListParams): HttpResponseFor = + list(runId, params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + runId: String, + params: StepListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + list(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed fun list(params: StepListParams): HttpResponseFor = list(params, RequestOptions.none()) 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 e4d41faf..bc2477b0 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 @@ -4,6 +4,7 @@ package com.openai.services.blocking.beta.threads.runs import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -20,6 +21,7 @@ import com.openai.models.beta.threads.runs.steps.StepListPage import com.openai.models.beta.threads.runs.steps.StepListPageResponse import com.openai.models.beta.threads.runs.steps.StepListParams import com.openai.models.beta.threads.runs.steps.StepRetrieveParams +import kotlin.jvm.optionals.getOrNull class StepServiceImpl internal constructor(private val clientOptions: ClientOptions) : StepService { @@ -54,6 +56,9 @@ class StepServiceImpl internal constructor(private val clientOptions: ClientOpti params: StepRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("stepId", params.stepId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -89,6 +94,9 @@ class StepServiceImpl internal constructor(private val clientOptions: ClientOpti params: StepListParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/ChatCompletionService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/ChatCompletionService.kt index 3f46a970..43f372d2 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/ChatCompletionService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/ChatCompletionService.kt @@ -15,6 +15,8 @@ import com.openai.models.chat.completions.ChatCompletionListPage import com.openai.models.chat.completions.ChatCompletionListParams import com.openai.models.chat.completions.ChatCompletionRetrieveParams import com.openai.models.chat.completions.ChatCompletionUpdateParams +import com.openai.models.chat.completions.StructuredChatCompletion +import com.openai.models.chat.completions.StructuredChatCompletionCreateParams import com.openai.services.blocking.chat.completions.MessageService interface ChatCompletionService { @@ -53,6 +55,30 @@ interface ChatCompletionService { requestOptions: RequestOptions = RequestOptions.none(), ): ChatCompletion + /** + * Creates a model response for the given chat conversation. The model's structured output in + * JSON form will be deserialized automatically into an instance of the class `T`. See the SDK + * documentation for more details. + * + * @see create + */ + fun create( + params: StructuredChatCompletionCreateParams + ): StructuredChatCompletion = create(params, RequestOptions.none()) + + /** + * Creates a model response for the given chat conversation. The model's structured output in + * JSON form will be deserialized automatically into an instance of the class `T`. See the SDK + * documentation for more details. + * + * @see create + */ + fun create( + params: StructuredChatCompletionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): StructuredChatCompletion = + StructuredChatCompletion(params.responseType, create(params.rawParams, requestOptions)) + /** * **Starting a new project?** We recommend trying * [Responses](https://platform.openai.com/docs/api-reference/responses) to take advantage of @@ -86,8 +112,22 @@ interface ChatCompletionService { * Get a stored chat completion. Only Chat Completions that have been created with the `store` * parameter set to `true` will be returned. */ - fun retrieve(params: ChatCompletionRetrieveParams): ChatCompletion = - retrieve(params, RequestOptions.none()) + fun retrieve(completionId: String): ChatCompletion = + retrieve(completionId, ChatCompletionRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + completionId: String, + params: ChatCompletionRetrieveParams = ChatCompletionRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ChatCompletion = + retrieve(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + completionId: String, + params: ChatCompletionRetrieveParams = ChatCompletionRetrieveParams.none(), + ): ChatCompletion = retrieve(completionId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -95,11 +135,31 @@ interface ChatCompletionService { requestOptions: RequestOptions = RequestOptions.none(), ): ChatCompletion + /** @see [retrieve] */ + fun retrieve(params: ChatCompletionRetrieveParams): ChatCompletion = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve(completionId: String, requestOptions: RequestOptions): ChatCompletion = + retrieve(completionId, ChatCompletionRetrieveParams.none(), requestOptions) + /** * 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. */ + fun update(completionId: String, params: ChatCompletionUpdateParams): ChatCompletion = + update(completionId, params, RequestOptions.none()) + + /** @see [update] */ + fun update( + completionId: String, + params: ChatCompletionUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ChatCompletion = + update(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [update] */ fun update(params: ChatCompletionUpdateParams): ChatCompletion = update(params, RequestOptions.none()) @@ -134,8 +194,22 @@ interface ChatCompletionService { * Delete a stored chat completion. Only Chat Completions that have been created with the * `store` parameter set to `true` can be deleted. */ - fun delete(params: ChatCompletionDeleteParams): ChatCompletionDeleted = - delete(params, RequestOptions.none()) + fun delete(completionId: String): ChatCompletionDeleted = + delete(completionId, ChatCompletionDeleteParams.none()) + + /** @see [delete] */ + fun delete( + completionId: String, + params: ChatCompletionDeleteParams = ChatCompletionDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ChatCompletionDeleted = + delete(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [delete] */ + fun delete( + completionId: String, + params: ChatCompletionDeleteParams = ChatCompletionDeleteParams.none(), + ): ChatCompletionDeleted = delete(completionId, params, RequestOptions.none()) /** @see [delete] */ fun delete( @@ -143,6 +217,14 @@ interface ChatCompletionService { requestOptions: RequestOptions = RequestOptions.none(), ): ChatCompletionDeleted + /** @see [delete] */ + fun delete(params: ChatCompletionDeleteParams): ChatCompletionDeleted = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + fun delete(completionId: String, requestOptions: RequestOptions): ChatCompletionDeleted = + delete(completionId, ChatCompletionDeleteParams.none(), requestOptions) + /** * A view of [ChatCompletionService] that provides access to raw HTTP responses for each method. */ @@ -187,8 +269,24 @@ interface ChatCompletionService { * the same as [ChatCompletionService.retrieve]. */ @MustBeClosed - fun retrieve(params: ChatCompletionRetrieveParams): HttpResponseFor = - retrieve(params, RequestOptions.none()) + fun retrieve(completionId: String): HttpResponseFor = + retrieve(completionId, ChatCompletionRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + completionId: String, + params: ChatCompletionRetrieveParams = ChatCompletionRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + completionId: String, + params: ChatCompletionRetrieveParams = ChatCompletionRetrieveParams.none(), + ): HttpResponseFor = retrieve(completionId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -197,11 +295,40 @@ interface ChatCompletionService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: ChatCompletionRetrieveParams): HttpResponseFor = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + completionId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + retrieve(completionId, ChatCompletionRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /chat/completions/{completion_id}`, but is * otherwise the same as [ChatCompletionService.update]. */ @MustBeClosed + fun update( + completionId: String, + params: ChatCompletionUpdateParams, + ): HttpResponseFor = update(completionId, params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + completionId: String, + params: ChatCompletionUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + update(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed fun update(params: ChatCompletionUpdateParams): HttpResponseFor = update(params, RequestOptions.none()) @@ -242,8 +369,25 @@ interface ChatCompletionService { * otherwise the same as [ChatCompletionService.delete]. */ @MustBeClosed - fun delete(params: ChatCompletionDeleteParams): HttpResponseFor = - delete(params, RequestOptions.none()) + fun delete(completionId: String): HttpResponseFor = + delete(completionId, ChatCompletionDeleteParams.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + completionId: String, + params: ChatCompletionDeleteParams = ChatCompletionDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + delete(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed + fun delete( + completionId: String, + params: ChatCompletionDeleteParams = ChatCompletionDeleteParams.none(), + ): HttpResponseFor = + delete(completionId, params, RequestOptions.none()) /** @see [delete] */ @MustBeClosed @@ -251,5 +395,18 @@ interface ChatCompletionService { params: ChatCompletionDeleteParams, requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + + /** @see [delete] */ + @MustBeClosed + fun delete(params: ChatCompletionDeleteParams): HttpResponseFor = + delete(params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + completionId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + delete(completionId, ChatCompletionDeleteParams.none(), requestOptions) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/ChatCompletionServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/ChatCompletionServiceImpl.kt index 0bffd8df..b07c2c6c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/ChatCompletionServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/ChatCompletionServiceImpl.kt @@ -5,6 +5,7 @@ package com.openai.services.blocking.chat import com.openai.core.ClientOptions import com.openai.core.JsonValue import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.mapJson @@ -174,6 +175,9 @@ class ChatCompletionServiceImpl internal constructor(private val clientOptions: params: ChatCompletionRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("completionId", params.completionId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -200,6 +204,9 @@ class ChatCompletionServiceImpl internal constructor(private val clientOptions: params: ChatCompletionUpdateParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("completionId", params.completionId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -266,6 +273,9 @@ class ChatCompletionServiceImpl internal constructor(private val clientOptions: params: ChatCompletionDeleteParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("completionId", params.completionId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) 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 b1161981..d4fc968c 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 @@ -19,7 +19,20 @@ interface MessageService { * 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. */ - fun list(params: MessageListParams): MessageListPage = list(params, RequestOptions.none()) + fun list(completionId: String): MessageListPage = list(completionId, MessageListParams.none()) + + /** @see [list] */ + fun list( + completionId: String, + params: MessageListParams = MessageListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): MessageListPage = list(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [list] */ + fun list( + completionId: String, + params: MessageListParams = MessageListParams.none(), + ): MessageListPage = list(completionId, params, RequestOptions.none()) /** @see [list] */ fun list( @@ -27,6 +40,13 @@ interface MessageService { requestOptions: RequestOptions = RequestOptions.none(), ): MessageListPage + /** @see [list] */ + fun list(params: MessageListParams): MessageListPage = list(params, RequestOptions.none()) + + /** @see [list] */ + fun list(completionId: String, requestOptions: RequestOptions): MessageListPage = + list(completionId, MessageListParams.none(), requestOptions) + /** A view of [MessageService] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -35,8 +55,24 @@ interface MessageService { * otherwise the same as [MessageService.list]. */ @MustBeClosed - fun list(params: MessageListParams): HttpResponseFor = - list(params, RequestOptions.none()) + fun list(completionId: String): HttpResponseFor = + list(completionId, MessageListParams.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + completionId: String, + params: MessageListParams = MessageListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + list(params.toBuilder().completionId(completionId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed + fun list( + completionId: String, + params: MessageListParams = MessageListParams.none(), + ): HttpResponseFor = list(completionId, params, RequestOptions.none()) /** @see [list] */ @MustBeClosed @@ -44,5 +80,18 @@ interface MessageService { params: MessageListParams, requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + + /** @see [list] */ + @MustBeClosed + fun list(params: MessageListParams): HttpResponseFor = + list(params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + completionId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + list(completionId, MessageListParams.none(), requestOptions) } } 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 c87f53ee..52b267d3 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 @@ -4,6 +4,7 @@ package com.openai.services.blocking.chat.completions import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -17,6 +18,7 @@ import com.openai.models.ErrorObject import com.openai.models.chat.completions.messages.MessageListPage import com.openai.models.chat.completions.messages.MessageListPageResponse import com.openai.models.chat.completions.messages.MessageListParams +import kotlin.jvm.optionals.getOrNull class MessageServiceImpl internal constructor(private val clientOptions: ClientOptions) : MessageService { @@ -44,6 +46,9 @@ class MessageServiceImpl internal constructor(private val clientOptions: ClientO params: MessageListParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("completionId", params.completionId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/RunService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/RunService.kt index c604c6c2..90dc28d4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/RunService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/RunService.kt @@ -26,7 +26,22 @@ interface RunService { fun outputItems(): OutputItemService - /** Create a new evaluation run. This is the endpoint that will kick off grading. */ + /** + * Kicks off a new run for a given evaluation, specifying the data source, and what model + * configuration to use to test. The datasource will be validated against the schema specified + * in the config of the evaluation. + */ + fun create(evalId: String, params: RunCreateParams): RunCreateResponse = + create(evalId, params, RequestOptions.none()) + + /** @see [create] */ + fun create( + evalId: String, + params: RunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): RunCreateResponse = create(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [create] */ fun create(params: RunCreateParams): RunCreateResponse = create(params, RequestOptions.none()) /** @see [create] */ @@ -36,6 +51,17 @@ interface RunService { ): RunCreateResponse /** Get an evaluation run by ID. */ + fun retrieve(runId: String, params: RunRetrieveParams): RunRetrieveResponse = + retrieve(runId, params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + runId: String, + params: RunRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): RunRetrieveResponse = retrieve(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [retrieve] */ fun retrieve(params: RunRetrieveParams): RunRetrieveResponse = retrieve(params, RequestOptions.none()) @@ -46,7 +72,18 @@ interface RunService { ): RunRetrieveResponse /** Get a list of runs for an evaluation. */ - fun list(params: RunListParams): RunListPage = list(params, RequestOptions.none()) + fun list(evalId: String): RunListPage = list(evalId, RunListParams.none()) + + /** @see [list] */ + fun list( + evalId: String, + params: RunListParams = RunListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): RunListPage = list(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [list] */ + fun list(evalId: String, params: RunListParams = RunListParams.none()): RunListPage = + list(evalId, params, RequestOptions.none()) /** @see [list] */ fun list( @@ -54,7 +91,25 @@ interface RunService { requestOptions: RequestOptions = RequestOptions.none(), ): RunListPage + /** @see [list] */ + fun list(params: RunListParams): RunListPage = list(params, RequestOptions.none()) + + /** @see [list] */ + fun list(evalId: String, requestOptions: RequestOptions): RunListPage = + list(evalId, RunListParams.none(), requestOptions) + /** Delete an eval run. */ + fun delete(runId: String, params: RunDeleteParams): RunDeleteResponse = + delete(runId, params, RequestOptions.none()) + + /** @see [delete] */ + fun delete( + runId: String, + params: RunDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): RunDeleteResponse = delete(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [delete] */ fun delete(params: RunDeleteParams): RunDeleteResponse = delete(params, RequestOptions.none()) /** @see [delete] */ @@ -64,6 +119,17 @@ interface RunService { ): RunDeleteResponse /** Cancel an ongoing evaluation run. */ + fun cancel(runId: String, params: RunCancelParams): RunCancelResponse = + cancel(runId, params, RequestOptions.none()) + + /** @see [cancel] */ + fun cancel( + runId: String, + params: RunCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): RunCancelResponse = cancel(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [cancel] */ fun cancel(params: RunCancelParams): RunCancelResponse = cancel(params, RequestOptions.none()) /** @see [cancel] */ @@ -82,6 +148,20 @@ interface RunService { * as [RunService.create]. */ @MustBeClosed + fun create(evalId: String, params: RunCreateParams): HttpResponseFor = + create(evalId, params, RequestOptions.none()) + + /** @see [create] */ + @MustBeClosed + fun create( + evalId: String, + params: RunCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + create(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [create] */ + @MustBeClosed fun create(params: RunCreateParams): HttpResponseFor = create(params, RequestOptions.none()) @@ -97,6 +177,22 @@ interface RunService { * the same as [RunService.retrieve]. */ @MustBeClosed + fun retrieve( + runId: String, + params: RunRetrieveParams, + ): HttpResponseFor = retrieve(runId, params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + runId: String, + params: RunRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve(params: RunRetrieveParams): HttpResponseFor = retrieve(params, RequestOptions.none()) @@ -112,8 +208,23 @@ interface RunService { * [RunService.list]. */ @MustBeClosed - fun list(params: RunListParams): HttpResponseFor = - list(params, RequestOptions.none()) + fun list(evalId: String): HttpResponseFor = list(evalId, RunListParams.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + evalId: String, + params: RunListParams = RunListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + list(params.toBuilder().evalId(evalId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed + fun list( + evalId: String, + params: RunListParams = RunListParams.none(), + ): HttpResponseFor = list(evalId, params, RequestOptions.none()) /** @see [list] */ @MustBeClosed @@ -122,11 +233,35 @@ interface RunService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [list] */ + @MustBeClosed + fun list(params: RunListParams): HttpResponseFor = + list(params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list(evalId: String, requestOptions: RequestOptions): HttpResponseFor = + list(evalId, RunListParams.none(), requestOptions) + /** * Returns a raw HTTP response for `delete /evals/{eval_id}/runs/{run_id}`, but is otherwise * the same as [RunService.delete]. */ @MustBeClosed + fun delete(runId: String, params: RunDeleteParams): HttpResponseFor = + delete(runId, params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + runId: String, + params: RunDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + delete(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed fun delete(params: RunDeleteParams): HttpResponseFor = delete(params, RequestOptions.none()) @@ -142,6 +277,20 @@ interface RunService { * the same as [RunService.cancel]. */ @MustBeClosed + fun cancel(runId: String, params: RunCancelParams): HttpResponseFor = + cancel(runId, params, RequestOptions.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + runId: String, + params: RunCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + cancel(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [cancel] */ + @MustBeClosed fun cancel(params: RunCancelParams): HttpResponseFor = cancel(params, RequestOptions.none()) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/RunServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/RunServiceImpl.kt index aa985644..ee64dae6 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/RunServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/RunServiceImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.blocking.evals import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -28,6 +29,7 @@ import com.openai.models.evals.runs.RunRetrieveParams import com.openai.models.evals.runs.RunRetrieveResponse import com.openai.services.blocking.evals.runs.OutputItemService import com.openai.services.blocking.evals.runs.OutputItemServiceImpl +import kotlin.jvm.optionals.getOrNull class RunServiceImpl internal constructor(private val clientOptions: ClientOptions) : RunService { @@ -91,6 +93,9 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio params: RunCreateParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("evalId", params.evalId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -119,6 +124,9 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio params: RunRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -146,6 +154,9 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio params: RunListParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("evalId", params.evalId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -179,6 +190,9 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio params: RunDeleteParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) @@ -206,6 +220,9 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio params: RunCancelParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/runs/OutputItemService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/runs/OutputItemService.kt index 5a1750da..59468f9e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/runs/OutputItemService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/runs/OutputItemService.kt @@ -18,6 +18,20 @@ interface OutputItemService { fun withRawResponse(): WithRawResponse /** Get an evaluation run output item by ID. */ + fun retrieve( + outputItemId: String, + params: OutputItemRetrieveParams, + ): OutputItemRetrieveResponse = retrieve(outputItemId, params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + outputItemId: String, + params: OutputItemRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): OutputItemRetrieveResponse = + retrieve(params.toBuilder().outputItemId(outputItemId).build(), requestOptions) + + /** @see [retrieve] */ fun retrieve(params: OutputItemRetrieveParams): OutputItemRetrieveResponse = retrieve(params, RequestOptions.none()) @@ -28,6 +42,17 @@ interface OutputItemService { ): OutputItemRetrieveResponse /** Get a list of output items for an evaluation run. */ + fun list(runId: String, params: OutputItemListParams): OutputItemListPage = + list(runId, params, RequestOptions.none()) + + /** @see [list] */ + fun list( + runId: String, + params: OutputItemListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): OutputItemListPage = list(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [list] */ fun list(params: OutputItemListParams): OutputItemListPage = list(params, RequestOptions.none()) /** @see [list] */ @@ -45,6 +70,23 @@ interface OutputItemService { * as [OutputItemService.retrieve]. */ @MustBeClosed + fun retrieve( + outputItemId: String, + params: OutputItemRetrieveParams, + ): HttpResponseFor = + retrieve(outputItemId, params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + outputItemId: String, + params: OutputItemRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().outputItemId(outputItemId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve( params: OutputItemRetrieveParams ): HttpResponseFor = retrieve(params, RequestOptions.none()) @@ -61,6 +103,20 @@ interface OutputItemService { * otherwise the same as [OutputItemService.list]. */ @MustBeClosed + fun list(runId: String, params: OutputItemListParams): HttpResponseFor = + list(runId, params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + runId: String, + params: OutputItemListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + list(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed fun list(params: OutputItemListParams): HttpResponseFor = list(params, RequestOptions.none()) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/runs/OutputItemServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/runs/OutputItemServiceImpl.kt index cce944dd..de2cfba3 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/runs/OutputItemServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/evals/runs/OutputItemServiceImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.blocking.evals.runs import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -19,6 +20,7 @@ import com.openai.models.evals.runs.outputitems.OutputItemListPageResponse import com.openai.models.evals.runs.outputitems.OutputItemListParams import com.openai.models.evals.runs.outputitems.OutputItemRetrieveParams import com.openai.models.evals.runs.outputitems.OutputItemRetrieveResponse +import kotlin.jvm.optionals.getOrNull class OutputItemServiceImpl internal constructor(private val clientOptions: ClientOptions) : OutputItemService { @@ -56,6 +58,9 @@ class OutputItemServiceImpl internal constructor(private val clientOptions: Clie params: OutputItemRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("outputItemId", params.outputItemId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -90,6 +95,9 @@ class OutputItemServiceImpl internal constructor(private val clientOptions: Clie params: OutputItemListParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/AlphaService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/AlphaService.kt new file mode 100644 index 00000000..6f892364 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/AlphaService.kt @@ -0,0 +1,21 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.finetuning + +import com.openai.services.blocking.finetuning.alpha.GraderService + +interface AlphaService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + fun graders(): GraderService + + /** A view of [AlphaService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun graders(): GraderService.WithRawResponse + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/AlphaServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/AlphaServiceImpl.kt new file mode 100644 index 00000000..372892c0 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/AlphaServiceImpl.kt @@ -0,0 +1,31 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.finetuning + +import com.openai.core.ClientOptions +import com.openai.services.blocking.finetuning.alpha.GraderService +import com.openai.services.blocking.finetuning.alpha.GraderServiceImpl + +class AlphaServiceImpl internal constructor(private val clientOptions: ClientOptions) : + AlphaService { + + private val withRawResponse: AlphaService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val graders: GraderService by lazy { GraderServiceImpl(clientOptions) } + + override fun withRawResponse(): AlphaService.WithRawResponse = withRawResponse + + override fun graders(): GraderService = graders + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + AlphaService.WithRawResponse { + + private val graders: GraderService.WithRawResponse by lazy { + GraderServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun graders(): GraderService.WithRawResponse = graders + } +} 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 1985fe05..785543c0 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 @@ -12,6 +12,8 @@ import com.openai.models.finetuning.jobs.JobListEventsPage import com.openai.models.finetuning.jobs.JobListEventsParams import com.openai.models.finetuning.jobs.JobListPage import com.openai.models.finetuning.jobs.JobListParams +import com.openai.models.finetuning.jobs.JobPauseParams +import com.openai.models.finetuning.jobs.JobResumeParams import com.openai.models.finetuning.jobs.JobRetrieveParams import com.openai.services.blocking.finetuning.jobs.CheckpointService @@ -46,7 +48,22 @@ interface JobService { * * [Learn more about fine-tuning](https://platform.openai.com/docs/guides/fine-tuning) */ - fun retrieve(params: JobRetrieveParams): FineTuningJob = retrieve(params, RequestOptions.none()) + fun retrieve(fineTuningJobId: String): FineTuningJob = + retrieve(fineTuningJobId, JobRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + fineTuningJobId: String, + params: JobRetrieveParams = JobRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): FineTuningJob = + retrieve(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [retrieve] */ + fun retrieve( + fineTuningJobId: String, + params: JobRetrieveParams = JobRetrieveParams.none(), + ): FineTuningJob = retrieve(fineTuningJobId, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -54,6 +71,13 @@ interface JobService { requestOptions: RequestOptions = RequestOptions.none(), ): FineTuningJob + /** @see [retrieve] */ + fun retrieve(params: JobRetrieveParams): FineTuningJob = retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve(fineTuningJobId: String, requestOptions: RequestOptions): FineTuningJob = + retrieve(fineTuningJobId, JobRetrieveParams.none(), requestOptions) + /** List your organization's fine-tuning jobs */ fun list(): JobListPage = list(JobListParams.none()) @@ -72,7 +96,22 @@ interface JobService { list(JobListParams.none(), requestOptions) /** Immediately cancel a fine-tune job. */ - fun cancel(params: JobCancelParams): FineTuningJob = cancel(params, RequestOptions.none()) + fun cancel(fineTuningJobId: String): FineTuningJob = + cancel(fineTuningJobId, JobCancelParams.none()) + + /** @see [cancel] */ + fun cancel( + fineTuningJobId: String, + params: JobCancelParams = JobCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): FineTuningJob = + cancel(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [cancel] */ + fun cancel( + fineTuningJobId: String, + params: JobCancelParams = JobCancelParams.none(), + ): FineTuningJob = cancel(fineTuningJobId, params, RequestOptions.none()) /** @see [cancel] */ fun cancel( @@ -80,9 +119,30 @@ interface JobService { requestOptions: RequestOptions = RequestOptions.none(), ): FineTuningJob + /** @see [cancel] */ + fun cancel(params: JobCancelParams): FineTuningJob = cancel(params, RequestOptions.none()) + + /** @see [cancel] */ + fun cancel(fineTuningJobId: String, requestOptions: RequestOptions): FineTuningJob = + cancel(fineTuningJobId, JobCancelParams.none(), requestOptions) + /** Get status updates for a fine-tuning job. */ - fun listEvents(params: JobListEventsParams): JobListEventsPage = - listEvents(params, RequestOptions.none()) + fun listEvents(fineTuningJobId: String): JobListEventsPage = + listEvents(fineTuningJobId, JobListEventsParams.none()) + + /** @see [listEvents] */ + fun listEvents( + fineTuningJobId: String, + params: JobListEventsParams = JobListEventsParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): JobListEventsPage = + listEvents(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [listEvents] */ + fun listEvents( + fineTuningJobId: String, + params: JobListEventsParams = JobListEventsParams.none(), + ): JobListEventsPage = listEvents(fineTuningJobId, params, RequestOptions.none()) /** @see [listEvents] */ fun listEvents( @@ -90,6 +150,76 @@ interface JobService { requestOptions: RequestOptions = RequestOptions.none(), ): JobListEventsPage + /** @see [listEvents] */ + fun listEvents(params: JobListEventsParams): JobListEventsPage = + listEvents(params, RequestOptions.none()) + + /** @see [listEvents] */ + fun listEvents(fineTuningJobId: String, requestOptions: RequestOptions): JobListEventsPage = + listEvents(fineTuningJobId, JobListEventsParams.none(), requestOptions) + + /** Pause a fine-tune job. */ + fun pause(fineTuningJobId: String): FineTuningJob = + pause(fineTuningJobId, JobPauseParams.none()) + + /** @see [pause] */ + fun pause( + fineTuningJobId: String, + params: JobPauseParams = JobPauseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): FineTuningJob = + pause(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [pause] */ + fun pause( + fineTuningJobId: String, + params: JobPauseParams = JobPauseParams.none(), + ): FineTuningJob = pause(fineTuningJobId, params, RequestOptions.none()) + + /** @see [pause] */ + fun pause( + params: JobPauseParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): FineTuningJob + + /** @see [pause] */ + fun pause(params: JobPauseParams): FineTuningJob = pause(params, RequestOptions.none()) + + /** @see [pause] */ + fun pause(fineTuningJobId: String, requestOptions: RequestOptions): FineTuningJob = + pause(fineTuningJobId, JobPauseParams.none(), requestOptions) + + /** Resume a fine-tune job. */ + fun resume(fineTuningJobId: String): FineTuningJob = + resume(fineTuningJobId, JobResumeParams.none()) + + /** @see [resume] */ + fun resume( + fineTuningJobId: String, + params: JobResumeParams = JobResumeParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): FineTuningJob = + resume(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [resume] */ + fun resume( + fineTuningJobId: String, + params: JobResumeParams = JobResumeParams.none(), + ): FineTuningJob = resume(fineTuningJobId, params, RequestOptions.none()) + + /** @see [resume] */ + fun resume( + params: JobResumeParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): FineTuningJob + + /** @see [resume] */ + fun resume(params: JobResumeParams): FineTuningJob = resume(params, RequestOptions.none()) + + /** @see [resume] */ + fun resume(fineTuningJobId: String, requestOptions: RequestOptions): FineTuningJob = + resume(fineTuningJobId, JobResumeParams.none(), requestOptions) + /** A view of [JobService] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -115,8 +245,24 @@ interface JobService { * otherwise the same as [JobService.retrieve]. */ @MustBeClosed - fun retrieve(params: JobRetrieveParams): HttpResponseFor = - retrieve(params, RequestOptions.none()) + fun retrieve(fineTuningJobId: String): HttpResponseFor = + retrieve(fineTuningJobId, JobRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fineTuningJobId: String, + params: JobRetrieveParams = JobRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fineTuningJobId: String, + params: JobRetrieveParams = JobRetrieveParams.none(), + ): HttpResponseFor = retrieve(fineTuningJobId, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -125,6 +271,19 @@ interface JobService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [retrieve] */ + @MustBeClosed + fun retrieve(params: JobRetrieveParams): HttpResponseFor = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + retrieve(fineTuningJobId, JobRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /fine_tuning/jobs`, but is otherwise the same as * [JobService.list]. @@ -153,8 +312,24 @@ interface JobService { * is otherwise the same as [JobService.cancel]. */ @MustBeClosed - fun cancel(params: JobCancelParams): HttpResponseFor = - cancel(params, RequestOptions.none()) + fun cancel(fineTuningJobId: String): HttpResponseFor = + cancel(fineTuningJobId, JobCancelParams.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + fineTuningJobId: String, + params: JobCancelParams = JobCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + cancel(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + fineTuningJobId: String, + params: JobCancelParams = JobCancelParams.none(), + ): HttpResponseFor = cancel(fineTuningJobId, params, RequestOptions.none()) /** @see [cancel] */ @MustBeClosed @@ -163,13 +338,43 @@ interface JobService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [cancel] */ + @MustBeClosed + fun cancel(params: JobCancelParams): HttpResponseFor = + cancel(params, RequestOptions.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + cancel(fineTuningJobId, JobCancelParams.none(), requestOptions) + /** * Returns a raw HTTP response for `get /fine_tuning/jobs/{fine_tuning_job_id}/events`, but * is otherwise the same as [JobService.listEvents]. */ @MustBeClosed - fun listEvents(params: JobListEventsParams): HttpResponseFor = - listEvents(params, RequestOptions.none()) + fun listEvents(fineTuningJobId: String): HttpResponseFor = + listEvents(fineTuningJobId, JobListEventsParams.none()) + + /** @see [listEvents] */ + @MustBeClosed + fun listEvents( + fineTuningJobId: String, + params: JobListEventsParams = JobListEventsParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + listEvents(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [listEvents] */ + @MustBeClosed + fun listEvents( + fineTuningJobId: String, + params: JobListEventsParams = JobListEventsParams.none(), + ): HttpResponseFor = + listEvents(fineTuningJobId, params, RequestOptions.none()) /** @see [listEvents] */ @MustBeClosed @@ -177,5 +382,106 @@ interface JobService { params: JobListEventsParams, requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + + /** @see [listEvents] */ + @MustBeClosed + fun listEvents(params: JobListEventsParams): HttpResponseFor = + listEvents(params, RequestOptions.none()) + + /** @see [listEvents] */ + @MustBeClosed + fun listEvents( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + listEvents(fineTuningJobId, JobListEventsParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /fine_tuning/jobs/{fine_tuning_job_id}/pause`, but + * is otherwise the same as [JobService.pause]. + */ + @MustBeClosed + fun pause(fineTuningJobId: String): HttpResponseFor = + pause(fineTuningJobId, JobPauseParams.none()) + + /** @see [pause] */ + @MustBeClosed + fun pause( + fineTuningJobId: String, + params: JobPauseParams = JobPauseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + pause(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [pause] */ + @MustBeClosed + fun pause( + fineTuningJobId: String, + params: JobPauseParams = JobPauseParams.none(), + ): HttpResponseFor = pause(fineTuningJobId, params, RequestOptions.none()) + + /** @see [pause] */ + @MustBeClosed + fun pause( + params: JobPauseParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see [pause] */ + @MustBeClosed + fun pause(params: JobPauseParams): HttpResponseFor = + pause(params, RequestOptions.none()) + + /** @see [pause] */ + @MustBeClosed + fun pause( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + pause(fineTuningJobId, JobPauseParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /fine_tuning/jobs/{fine_tuning_job_id}/resume`, but + * is otherwise the same as [JobService.resume]. + */ + @MustBeClosed + fun resume(fineTuningJobId: String): HttpResponseFor = + resume(fineTuningJobId, JobResumeParams.none()) + + /** @see [resume] */ + @MustBeClosed + fun resume( + fineTuningJobId: String, + params: JobResumeParams = JobResumeParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + resume(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [resume] */ + @MustBeClosed + fun resume( + fineTuningJobId: String, + params: JobResumeParams = JobResumeParams.none(), + ): HttpResponseFor = resume(fineTuningJobId, params, RequestOptions.none()) + + /** @see [resume] */ + @MustBeClosed + fun resume( + params: JobResumeParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see [resume] */ + @MustBeClosed + fun resume(params: JobResumeParams): HttpResponseFor = + resume(params, RequestOptions.none()) + + /** @see [resume] */ + @MustBeClosed + fun resume( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + resume(fineTuningJobId, JobResumeParams.none(), requestOptions) } } 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 64319c98..ba95b5d3 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 @@ -4,6 +4,7 @@ package com.openai.services.blocking.finetuning import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -24,9 +25,12 @@ import com.openai.models.finetuning.jobs.JobListEventsParams import com.openai.models.finetuning.jobs.JobListPage import com.openai.models.finetuning.jobs.JobListPageResponse import com.openai.models.finetuning.jobs.JobListParams +import com.openai.models.finetuning.jobs.JobPauseParams +import com.openai.models.finetuning.jobs.JobResumeParams import com.openai.models.finetuning.jobs.JobRetrieveParams import com.openai.services.blocking.finetuning.jobs.CheckpointService import com.openai.services.blocking.finetuning.jobs.CheckpointServiceImpl +import kotlin.jvm.optionals.getOrNull class JobServiceImpl internal constructor(private val clientOptions: ClientOptions) : JobService { @@ -66,6 +70,14 @@ class JobServiceImpl internal constructor(private val clientOptions: ClientOptio // get /fine_tuning/jobs/{fine_tuning_job_id}/events withRawResponse().listEvents(params, requestOptions).parse() + override fun pause(params: JobPauseParams, requestOptions: RequestOptions): FineTuningJob = + // post /fine_tuning/jobs/{fine_tuning_job_id}/pause + withRawResponse().pause(params, requestOptions).parse() + + override fun resume(params: JobResumeParams, requestOptions: RequestOptions): FineTuningJob = + // post /fine_tuning/jobs/{fine_tuning_job_id}/resume + withRawResponse().resume(params, requestOptions).parse() + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : JobService.WithRawResponse { @@ -111,6 +123,9 @@ class JobServiceImpl internal constructor(private val clientOptions: ClientOptio params: JobRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTuningJobId", params.fineTuningJobId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -171,6 +186,9 @@ class JobServiceImpl internal constructor(private val clientOptions: ClientOptio params: JobCancelParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTuningJobId", params.fineTuningJobId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -199,6 +217,9 @@ class JobServiceImpl internal constructor(private val clientOptions: ClientOptio params: JobListEventsParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTuningJobId", params.fineTuningJobId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -224,5 +245,65 @@ class JobServiceImpl internal constructor(private val clientOptions: ClientOptio } } } + + private val pauseHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun pause( + params: JobPauseParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTuningJobId", params.fineTuningJobId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("fine_tuning", "jobs", params._pathParam(0), "pause") + .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 { pauseHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val resumeHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun resume( + params: JobResumeParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTuningJobId", params.fineTuningJobId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("fine_tuning", "jobs", params._pathParam(0), "resume") + .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 { resumeHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/MethodService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/MethodService.kt new file mode 100644 index 00000000..7d829c06 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/MethodService.kt @@ -0,0 +1,14 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.finetuning + +interface MethodService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** A view of [MethodService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/MethodServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/MethodServiceImpl.kt new file mode 100644 index 00000000..a7750b2c --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/MethodServiceImpl.kt @@ -0,0 +1,18 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.finetuning + +import com.openai.core.ClientOptions + +class MethodServiceImpl internal constructor(private val clientOptions: ClientOptions) : + MethodService { + + private val withRawResponse: MethodService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): MethodService.WithRawResponse = withRawResponse + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + MethodService.WithRawResponse +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/alpha/GraderService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/alpha/GraderService.kt new file mode 100644 index 00000000..f6e4fe7c --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/alpha/GraderService.kt @@ -0,0 +1,72 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.finetuning.alpha + +import com.google.errorprone.annotations.MustBeClosed +import com.openai.core.RequestOptions +import com.openai.core.http.HttpResponseFor +import com.openai.models.finetuning.alpha.graders.GraderRunParams +import com.openai.models.finetuning.alpha.graders.GraderRunResponse +import com.openai.models.finetuning.alpha.graders.GraderValidateParams +import com.openai.models.finetuning.alpha.graders.GraderValidateResponse + +interface GraderService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** Run a grader. */ + fun run(params: GraderRunParams): GraderRunResponse = run(params, RequestOptions.none()) + + /** @see [run] */ + fun run( + params: GraderRunParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): GraderRunResponse + + /** Validate a grader. */ + fun validate(params: GraderValidateParams): GraderValidateResponse = + validate(params, RequestOptions.none()) + + /** @see [validate] */ + fun validate( + params: GraderValidateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): GraderValidateResponse + + /** A view of [GraderService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a raw HTTP response for `post /fine_tuning/alpha/graders/run`, but is otherwise + * the same as [GraderService.run]. + */ + @MustBeClosed + fun run(params: GraderRunParams): HttpResponseFor = + run(params, RequestOptions.none()) + + /** @see [run] */ + @MustBeClosed + fun run( + params: GraderRunParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /fine_tuning/alpha/graders/validate`, but is + * otherwise the same as [GraderService.validate]. + */ + @MustBeClosed + fun validate(params: GraderValidateParams): HttpResponseFor = + validate(params, RequestOptions.none()) + + /** @see [validate] */ + @MustBeClosed + fun validate( + params: GraderValidateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/alpha/GraderServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/alpha/GraderServiceImpl.kt new file mode 100644 index 00000000..4a9e21fc --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/alpha/GraderServiceImpl.kt @@ -0,0 +1,103 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.finetuning.alpha + +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.json +import com.openai.core.http.parseable +import com.openai.core.prepare +import com.openai.models.ErrorObject +import com.openai.models.finetuning.alpha.graders.GraderRunParams +import com.openai.models.finetuning.alpha.graders.GraderRunResponse +import com.openai.models.finetuning.alpha.graders.GraderValidateParams +import com.openai.models.finetuning.alpha.graders.GraderValidateResponse + +class GraderServiceImpl internal constructor(private val clientOptions: ClientOptions) : + GraderService { + + private val withRawResponse: GraderService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): GraderService.WithRawResponse = withRawResponse + + override fun run(params: GraderRunParams, requestOptions: RequestOptions): GraderRunResponse = + // post /fine_tuning/alpha/graders/run + withRawResponse().run(params, requestOptions).parse() + + override fun validate( + params: GraderValidateParams, + requestOptions: RequestOptions, + ): GraderValidateResponse = + // post /fine_tuning/alpha/graders/validate + withRawResponse().validate(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + GraderService.WithRawResponse { + + private val errorHandler: Handler = errorHandler(clientOptions.jsonMapper) + + private val runHandler: Handler = + jsonHandler(clientOptions.jsonMapper).withErrorHandler(errorHandler) + + override fun run( + params: GraderRunParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("fine_tuning", "alpha", "graders", "run") + .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 { runHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val validateHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + .withErrorHandler(errorHandler) + + override fun validate( + params: GraderValidateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegments("fine_tuning", "alpha", "graders", "validate") + .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 { validateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/checkpoints/PermissionService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/checkpoints/PermissionService.kt index 3271fff5..547aa265 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/checkpoints/PermissionService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/checkpoints/PermissionService.kt @@ -25,6 +25,23 @@ interface PermissionService { * This enables organization owners to share fine-tuned models with other projects in their * organization. */ + fun create( + fineTunedModelCheckpoint: String, + params: PermissionCreateParams, + ): PermissionCreatePage = create(fineTunedModelCheckpoint, params, RequestOptions.none()) + + /** @see [create] */ + fun create( + fineTunedModelCheckpoint: String, + params: PermissionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): PermissionCreatePage = + create( + params.toBuilder().fineTunedModelCheckpoint(fineTunedModelCheckpoint).build(), + requestOptions, + ) + + /** @see [create] */ fun create(params: PermissionCreateParams): PermissionCreatePage = create(params, RequestOptions.none()) @@ -40,8 +57,26 @@ interface PermissionService { * Organization owners can use this endpoint to view all permissions for a fine-tuned model * checkpoint. */ - fun retrieve(params: PermissionRetrieveParams): PermissionRetrieveResponse = - retrieve(params, RequestOptions.none()) + fun retrieve(fineTunedModelCheckpoint: String): PermissionRetrieveResponse = + retrieve(fineTunedModelCheckpoint, PermissionRetrieveParams.none()) + + /** @see [retrieve] */ + fun retrieve( + fineTunedModelCheckpoint: String, + params: PermissionRetrieveParams = PermissionRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): PermissionRetrieveResponse = + retrieve( + params.toBuilder().fineTunedModelCheckpoint(fineTunedModelCheckpoint).build(), + requestOptions, + ) + + /** @see [retrieve] */ + fun retrieve( + fineTunedModelCheckpoint: String, + params: PermissionRetrieveParams = PermissionRetrieveParams.none(), + ): PermissionRetrieveResponse = + retrieve(fineTunedModelCheckpoint, params, RequestOptions.none()) /** @see [retrieve] */ fun retrieve( @@ -49,12 +84,35 @@ interface PermissionService { requestOptions: RequestOptions = RequestOptions.none(), ): PermissionRetrieveResponse + /** @see [retrieve] */ + fun retrieve(params: PermissionRetrieveParams): PermissionRetrieveResponse = + retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + fineTunedModelCheckpoint: String, + requestOptions: RequestOptions, + ): PermissionRetrieveResponse = + retrieve(fineTunedModelCheckpoint, PermissionRetrieveParams.none(), requestOptions) + /** * **NOTE:** This endpoint requires an [admin API key](../admin-api-keys). * * Organization owners can use this endpoint to delete a permission for a fine-tuned model * checkpoint. */ + fun delete(permissionId: String, params: PermissionDeleteParams): PermissionDeleteResponse = + delete(permissionId, params, RequestOptions.none()) + + /** @see [delete] */ + fun delete( + permissionId: String, + params: PermissionDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): PermissionDeleteResponse = + delete(params.toBuilder().permissionId(permissionId).build(), requestOptions) + + /** @see [delete] */ fun delete(params: PermissionDeleteParams): PermissionDeleteResponse = delete(params, RequestOptions.none()) @@ -73,6 +131,26 @@ interface PermissionService { * same as [PermissionService.create]. */ @MustBeClosed + fun create( + fineTunedModelCheckpoint: String, + params: PermissionCreateParams, + ): HttpResponseFor = + create(fineTunedModelCheckpoint, params, RequestOptions.none()) + + /** @see [create] */ + @MustBeClosed + fun create( + fineTunedModelCheckpoint: String, + params: PermissionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + create( + params.toBuilder().fineTunedModelCheckpoint(fineTunedModelCheckpoint).build(), + requestOptions, + ) + + /** @see [create] */ + @MustBeClosed fun create(params: PermissionCreateParams): HttpResponseFor = create(params, RequestOptions.none()) @@ -90,8 +168,29 @@ interface PermissionService { */ @MustBeClosed fun retrieve( - params: PermissionRetrieveParams - ): HttpResponseFor = retrieve(params, RequestOptions.none()) + fineTunedModelCheckpoint: String + ): HttpResponseFor = + retrieve(fineTunedModelCheckpoint, PermissionRetrieveParams.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fineTunedModelCheckpoint: String, + params: PermissionRetrieveParams = PermissionRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve( + params.toBuilder().fineTunedModelCheckpoint(fineTunedModelCheckpoint).build(), + requestOptions, + ) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fineTunedModelCheckpoint: String, + params: PermissionRetrieveParams = PermissionRetrieveParams.none(), + ): HttpResponseFor = + retrieve(fineTunedModelCheckpoint, params, RequestOptions.none()) /** @see [retrieve] */ @MustBeClosed @@ -100,12 +199,43 @@ interface PermissionService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + params: PermissionRetrieveParams + ): HttpResponseFor = retrieve(params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fineTunedModelCheckpoint: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + retrieve(fineTunedModelCheckpoint, PermissionRetrieveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `delete * /fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions/{permission_id}`, but * is otherwise the same as [PermissionService.delete]. */ @MustBeClosed + fun delete( + permissionId: String, + params: PermissionDeleteParams, + ): HttpResponseFor = + delete(permissionId, params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + permissionId: String, + params: PermissionDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + delete(params.toBuilder().permissionId(permissionId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed fun delete(params: PermissionDeleteParams): HttpResponseFor = delete(params, RequestOptions.none()) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/checkpoints/PermissionServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/checkpoints/PermissionServiceImpl.kt index 7b809268..6ac2f12e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/checkpoints/PermissionServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/checkpoints/PermissionServiceImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.blocking.finetuning.checkpoints import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -22,6 +23,7 @@ import com.openai.models.finetuning.checkpoints.permissions.PermissionDeletePara import com.openai.models.finetuning.checkpoints.permissions.PermissionDeleteResponse import com.openai.models.finetuning.checkpoints.permissions.PermissionRetrieveParams import com.openai.models.finetuning.checkpoints.permissions.PermissionRetrieveResponse +import kotlin.jvm.optionals.getOrNull class PermissionServiceImpl internal constructor(private val clientOptions: ClientOptions) : PermissionService { @@ -66,6 +68,9 @@ class PermissionServiceImpl internal constructor(private val clientOptions: Clie params: PermissionCreateParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTunedModelCheckpoint", params.fineTunedModelCheckpoint().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -106,6 +111,9 @@ class PermissionServiceImpl internal constructor(private val clientOptions: Clie params: PermissionRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTunedModelCheckpoint", params.fineTunedModelCheckpoint().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -138,6 +146,9 @@ class PermissionServiceImpl internal constructor(private val clientOptions: Clie params: PermissionDeleteParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("permissionId", params.permissionId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) 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 125b06c9..48717773 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 @@ -16,7 +16,22 @@ interface CheckpointService { fun withRawResponse(): WithRawResponse /** List checkpoints for a fine-tuning job. */ - fun list(params: CheckpointListParams): CheckpointListPage = list(params, RequestOptions.none()) + fun list(fineTuningJobId: String): CheckpointListPage = + list(fineTuningJobId, CheckpointListParams.none()) + + /** @see [list] */ + fun list( + fineTuningJobId: String, + params: CheckpointListParams = CheckpointListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CheckpointListPage = + list(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [list] */ + fun list( + fineTuningJobId: String, + params: CheckpointListParams = CheckpointListParams.none(), + ): CheckpointListPage = list(fineTuningJobId, params, RequestOptions.none()) /** @see [list] */ fun list( @@ -24,6 +39,13 @@ interface CheckpointService { requestOptions: RequestOptions = RequestOptions.none(), ): CheckpointListPage + /** @see [list] */ + fun list(params: CheckpointListParams): CheckpointListPage = list(params, RequestOptions.none()) + + /** @see [list] */ + fun list(fineTuningJobId: String, requestOptions: RequestOptions): CheckpointListPage = + list(fineTuningJobId, CheckpointListParams.none(), requestOptions) + /** A view of [CheckpointService] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -32,8 +54,25 @@ interface CheckpointService { * but is otherwise the same as [CheckpointService.list]. */ @MustBeClosed - fun list(params: CheckpointListParams): HttpResponseFor = - list(params, RequestOptions.none()) + fun list(fineTuningJobId: String): HttpResponseFor = + list(fineTuningJobId, CheckpointListParams.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + fineTuningJobId: String, + params: CheckpointListParams = CheckpointListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + list(params.toBuilder().fineTuningJobId(fineTuningJobId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed + fun list( + fineTuningJobId: String, + params: CheckpointListParams = CheckpointListParams.none(), + ): HttpResponseFor = + list(fineTuningJobId, params, RequestOptions.none()) /** @see [list] */ @MustBeClosed @@ -41,5 +80,18 @@ interface CheckpointService { params: CheckpointListParams, requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + + /** @see [list] */ + @MustBeClosed + fun list(params: CheckpointListParams): HttpResponseFor = + list(params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + fineTuningJobId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + list(fineTuningJobId, CheckpointListParams.none(), requestOptions) } } 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 34d91dc8..23fe2d2d 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 @@ -4,6 +4,7 @@ package com.openai.services.blocking.finetuning.jobs import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -17,6 +18,7 @@ import com.openai.models.ErrorObject import com.openai.models.finetuning.jobs.checkpoints.CheckpointListPage import com.openai.models.finetuning.jobs.checkpoints.CheckpointListPageResponse import com.openai.models.finetuning.jobs.checkpoints.CheckpointListParams +import kotlin.jvm.optionals.getOrNull class CheckpointServiceImpl internal constructor(private val clientOptions: ClientOptions) : CheckpointService { @@ -47,6 +49,9 @@ class CheckpointServiceImpl internal constructor(private val clientOptions: Clie params: CheckpointListParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fineTuningJobId", params.fineTuningJobId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/graders/GraderModelService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/graders/GraderModelService.kt new file mode 100644 index 00000000..40f334c4 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/graders/GraderModelService.kt @@ -0,0 +1,16 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.graders + +interface GraderModelService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * A view of [GraderModelService] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/graders/GraderModelServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/graders/GraderModelServiceImpl.kt new file mode 100644 index 00000000..01f3f0d1 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/graders/GraderModelServiceImpl.kt @@ -0,0 +1,18 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.graders + +import com.openai.core.ClientOptions + +class GraderModelServiceImpl internal constructor(private val clientOptions: ClientOptions) : + GraderModelService { + + private val withRawResponse: GraderModelService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): GraderModelService.WithRawResponse = withRawResponse + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + GraderModelService.WithRawResponse +} diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/responses/InputItemService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/responses/InputItemService.kt index bbfab36e..81690e16 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/responses/InputItemService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/responses/InputItemService.kt @@ -16,7 +16,20 @@ interface InputItemService { fun withRawResponse(): WithRawResponse /** Returns a list of input items for a given response. */ - fun list(params: InputItemListParams): InputItemListPage = list(params, RequestOptions.none()) + fun list(responseId: String): InputItemListPage = list(responseId, InputItemListParams.none()) + + /** @see [list] */ + fun list( + responseId: String, + params: InputItemListParams = InputItemListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): InputItemListPage = list(params.toBuilder().responseId(responseId).build(), requestOptions) + + /** @see [list] */ + fun list( + responseId: String, + params: InputItemListParams = InputItemListParams.none(), + ): InputItemListPage = list(responseId, params, RequestOptions.none()) /** @see [list] */ fun list( @@ -24,6 +37,13 @@ interface InputItemService { requestOptions: RequestOptions = RequestOptions.none(), ): InputItemListPage + /** @see [list] */ + fun list(params: InputItemListParams): InputItemListPage = list(params, RequestOptions.none()) + + /** @see [list] */ + fun list(responseId: String, requestOptions: RequestOptions): InputItemListPage = + list(responseId, InputItemListParams.none(), requestOptions) + /** A view of [InputItemService] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -32,8 +52,24 @@ interface InputItemService { * otherwise the same as [InputItemService.list]. */ @MustBeClosed - fun list(params: InputItemListParams): HttpResponseFor = - list(params, RequestOptions.none()) + fun list(responseId: String): HttpResponseFor = + list(responseId, InputItemListParams.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + responseId: String, + params: InputItemListParams = InputItemListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + list(params.toBuilder().responseId(responseId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed + fun list( + responseId: String, + params: InputItemListParams = InputItemListParams.none(), + ): HttpResponseFor = list(responseId, params, RequestOptions.none()) /** @see [list] */ @MustBeClosed @@ -41,5 +77,18 @@ interface InputItemService { params: InputItemListParams, requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + + /** @see [list] */ + @MustBeClosed + fun list(params: InputItemListParams): HttpResponseFor = + list(params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + responseId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + list(responseId, InputItemListParams.none(), requestOptions) } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/responses/InputItemServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/responses/InputItemServiceImpl.kt index 19e1a397..aa535bbc 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/responses/InputItemServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/responses/InputItemServiceImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.blocking.responses import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -17,6 +18,7 @@ import com.openai.models.ErrorObject import com.openai.models.responses.inputitems.InputItemListPage import com.openai.models.responses.inputitems.InputItemListParams import com.openai.models.responses.inputitems.ResponseItemList +import kotlin.jvm.optionals.getOrNull class InputItemServiceImpl internal constructor(private val clientOptions: ClientOptions) : InputItemService { @@ -46,6 +48,9 @@ class InputItemServiceImpl internal constructor(private val clientOptions: Clien params: InputItemListParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("responseId", params.responseId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) 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 1c157e70..a1990493 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 @@ -27,6 +27,17 @@ interface PartService { * Parts when you * [complete the Upload](https://platform.openai.com/docs/api-reference/uploads/complete). */ + fun create(uploadId: String, params: PartCreateParams): UploadPart = + create(uploadId, params, RequestOptions.none()) + + /** @see [create] */ + fun create( + uploadId: String, + params: PartCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): UploadPart = create(params.toBuilder().uploadId(uploadId).build(), requestOptions) + + /** @see [create] */ fun create(params: PartCreateParams): UploadPart = create(params, RequestOptions.none()) /** @see [create] */ @@ -43,6 +54,20 @@ interface PartService { * same as [PartService.create]. */ @MustBeClosed + fun create(uploadId: String, params: PartCreateParams): HttpResponseFor = + create(uploadId, params, RequestOptions.none()) + + /** @see [create] */ + @MustBeClosed + fun create( + uploadId: String, + params: PartCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + create(params.toBuilder().uploadId(uploadId).build(), requestOptions) + + /** @see [create] */ + @MustBeClosed fun create(params: PartCreateParams): HttpResponseFor = create(params, RequestOptions.none()) 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 0f616b5d..23bf0b68 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 @@ -4,6 +4,7 @@ package com.openai.services.blocking.uploads import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -17,6 +18,7 @@ import com.openai.core.prepare import com.openai.models.ErrorObject import com.openai.models.uploads.parts.PartCreateParams import com.openai.models.uploads.parts.UploadPart +import kotlin.jvm.optionals.getOrNull class PartServiceImpl internal constructor(private val clientOptions: ClientOptions) : PartService { @@ -42,6 +44,9 @@ class PartServiceImpl internal constructor(private val clientOptions: ClientOpti params: PartCreateParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("uploadId", params.uploadId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileBatchService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileBatchService.kt index 5bf2fe58..2f9f55f5 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileBatchService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileBatchService.kt @@ -20,6 +20,18 @@ interface FileBatchService { fun withRawResponse(): WithRawResponse /** Create a vector store file batch. */ + fun create(vectorStoreId: String, params: FileBatchCreateParams): VectorStoreFileBatch = + create(vectorStoreId, params, RequestOptions.none()) + + /** @see [create] */ + fun create( + vectorStoreId: String, + params: FileBatchCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): VectorStoreFileBatch = + create(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [create] */ fun create(params: FileBatchCreateParams): VectorStoreFileBatch = create(params, RequestOptions.none()) @@ -30,6 +42,17 @@ interface FileBatchService { ): VectorStoreFileBatch /** Retrieves a vector store file batch. */ + fun retrieve(batchId: String, params: FileBatchRetrieveParams): VectorStoreFileBatch = + retrieve(batchId, params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + batchId: String, + params: FileBatchRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): VectorStoreFileBatch = retrieve(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [retrieve] */ fun retrieve(params: FileBatchRetrieveParams): VectorStoreFileBatch = retrieve(params, RequestOptions.none()) @@ -43,6 +66,17 @@ interface FileBatchService { * Cancel a vector store file batch. This attempts to cancel the processing of files in this * batch as soon as possible. */ + fun cancel(batchId: String, params: FileBatchCancelParams): VectorStoreFileBatch = + cancel(batchId, params, RequestOptions.none()) + + /** @see [cancel] */ + fun cancel( + batchId: String, + params: FileBatchCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): VectorStoreFileBatch = cancel(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [cancel] */ fun cancel(params: FileBatchCancelParams): VectorStoreFileBatch = cancel(params, RequestOptions.none()) @@ -53,6 +87,18 @@ interface FileBatchService { ): VectorStoreFileBatch /** Returns a list of vector store files in a batch. */ + fun listFiles(batchId: String, params: FileBatchListFilesParams): FileBatchListFilesPage = + listFiles(batchId, params, RequestOptions.none()) + + /** @see [listFiles] */ + fun listFiles( + batchId: String, + params: FileBatchListFilesParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): FileBatchListFilesPage = + listFiles(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [listFiles] */ fun listFiles(params: FileBatchListFilesParams): FileBatchListFilesPage = listFiles(params, RequestOptions.none()) @@ -70,6 +116,23 @@ interface FileBatchService { * is otherwise the same as [FileBatchService.create]. */ @MustBeClosed + fun create( + vectorStoreId: String, + params: FileBatchCreateParams, + ): HttpResponseFor = + create(vectorStoreId, params, RequestOptions.none()) + + /** @see [create] */ + @MustBeClosed + fun create( + vectorStoreId: String, + params: FileBatchCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + create(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [create] */ + @MustBeClosed fun create(params: FileBatchCreateParams): HttpResponseFor = create(params, RequestOptions.none()) @@ -86,6 +149,22 @@ interface FileBatchService { * [FileBatchService.retrieve]. */ @MustBeClosed + fun retrieve( + batchId: String, + params: FileBatchRetrieveParams, + ): HttpResponseFor = retrieve(batchId, params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + batchId: String, + params: FileBatchRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve(params: FileBatchRetrieveParams): HttpResponseFor = retrieve(params, RequestOptions.none()) @@ -102,6 +181,22 @@ interface FileBatchService { * same as [FileBatchService.cancel]. */ @MustBeClosed + fun cancel( + batchId: String, + params: FileBatchCancelParams, + ): HttpResponseFor = cancel(batchId, params, RequestOptions.none()) + + /** @see [cancel] */ + @MustBeClosed + fun cancel( + batchId: String, + params: FileBatchCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + cancel(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [cancel] */ + @MustBeClosed fun cancel(params: FileBatchCancelParams): HttpResponseFor = cancel(params, RequestOptions.none()) @@ -118,6 +213,23 @@ interface FileBatchService { * same as [FileBatchService.listFiles]. */ @MustBeClosed + fun listFiles( + batchId: String, + params: FileBatchListFilesParams, + ): HttpResponseFor = + listFiles(batchId, params, RequestOptions.none()) + + /** @see [listFiles] */ + @MustBeClosed + fun listFiles( + batchId: String, + params: FileBatchListFilesParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + listFiles(params.toBuilder().batchId(batchId).build(), requestOptions) + + /** @see [listFiles] */ + @MustBeClosed fun listFiles(params: FileBatchListFilesParams): HttpResponseFor = listFiles(params, RequestOptions.none()) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileBatchServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileBatchServiceImpl.kt index 516848c4..edfa1dd2 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileBatchServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileBatchServiceImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.blocking.vectorstores import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -23,6 +24,7 @@ import com.openai.models.vectorstores.filebatches.FileBatchListFilesPageResponse import com.openai.models.vectorstores.filebatches.FileBatchListFilesParams import com.openai.models.vectorstores.filebatches.FileBatchRetrieveParams import com.openai.models.vectorstores.filebatches.VectorStoreFileBatch +import kotlin.jvm.optionals.getOrNull class FileBatchServiceImpl internal constructor(private val clientOptions: ClientOptions) : FileBatchService { @@ -79,6 +81,9 @@ class FileBatchServiceImpl internal constructor(private val clientOptions: Clien params: FileBatchCreateParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("vectorStoreId", params.vectorStoreId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -108,6 +113,9 @@ class FileBatchServiceImpl internal constructor(private val clientOptions: Clien params: FileBatchRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("batchId", params.batchId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -141,6 +149,9 @@ class FileBatchServiceImpl internal constructor(private val clientOptions: Clien params: FileBatchCancelParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("batchId", params.batchId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -176,6 +187,9 @@ class FileBatchServiceImpl internal constructor(private val clientOptions: Clien params: FileBatchListFilesParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("batchId", params.batchId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileService.kt index 1d081a12..9cb49b69 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileService.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileService.kt @@ -28,6 +28,18 @@ interface FileService { * [File](https://platform.openai.com/docs/api-reference/files) to a * [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object). */ + fun create(vectorStoreId: String, params: FileCreateParams): VectorStoreFile = + create(vectorStoreId, params, RequestOptions.none()) + + /** @see [create] */ + fun create( + vectorStoreId: String, + params: FileCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): VectorStoreFile = + create(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [create] */ fun create(params: FileCreateParams): VectorStoreFile = create(params, RequestOptions.none()) /** @see [create] */ @@ -37,6 +49,17 @@ interface FileService { ): VectorStoreFile /** Retrieves a vector store file. */ + fun retrieve(fileId: String, params: FileRetrieveParams): VectorStoreFile = + retrieve(fileId, params, RequestOptions.none()) + + /** @see [retrieve] */ + fun retrieve( + fileId: String, + params: FileRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): VectorStoreFile = retrieve(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [retrieve] */ fun retrieve(params: FileRetrieveParams): VectorStoreFile = retrieve(params, RequestOptions.none()) @@ -47,6 +70,17 @@ interface FileService { ): VectorStoreFile /** Update attributes on a vector store file. */ + fun update(fileId: String, params: FileUpdateParams): VectorStoreFile = + update(fileId, params, RequestOptions.none()) + + /** @see [update] */ + fun update( + fileId: String, + params: FileUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): VectorStoreFile = update(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [update] */ fun update(params: FileUpdateParams): VectorStoreFile = update(params, RequestOptions.none()) /** @see [update] */ @@ -56,7 +90,18 @@ interface FileService { ): VectorStoreFile /** Returns a list of vector store files. */ - fun list(params: FileListParams): FileListPage = list(params, RequestOptions.none()) + fun list(vectorStoreId: String): FileListPage = list(vectorStoreId, FileListParams.none()) + + /** @see [list] */ + fun list( + vectorStoreId: String, + params: FileListParams = FileListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): FileListPage = list(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [list] */ + fun list(vectorStoreId: String, params: FileListParams = FileListParams.none()): FileListPage = + list(vectorStoreId, params, RequestOptions.none()) /** @see [list] */ fun list( @@ -64,11 +109,29 @@ interface FileService { requestOptions: RequestOptions = RequestOptions.none(), ): FileListPage + /** @see [list] */ + fun list(params: FileListParams): FileListPage = list(params, RequestOptions.none()) + + /** @see [list] */ + fun list(vectorStoreId: String, requestOptions: RequestOptions): FileListPage = + list(vectorStoreId, FileListParams.none(), requestOptions) + /** * 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. */ + fun delete(fileId: String, params: FileDeleteParams): VectorStoreFileDeleted = + delete(fileId, params, RequestOptions.none()) + + /** @see [delete] */ + fun delete( + fileId: String, + params: FileDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): VectorStoreFileDeleted = delete(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [delete] */ fun delete(params: FileDeleteParams): VectorStoreFileDeleted = delete(params, RequestOptions.none()) @@ -79,6 +142,17 @@ interface FileService { ): VectorStoreFileDeleted /** Retrieve the parsed contents of a vector store file. */ + fun content(fileId: String, params: FileContentParams): FileContentPage = + content(fileId, params, RequestOptions.none()) + + /** @see [content] */ + fun content( + fileId: String, + params: FileContentParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): FileContentPage = content(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [content] */ fun content(params: FileContentParams): FileContentPage = content(params, RequestOptions.none()) /** @see [content] */ @@ -95,6 +169,22 @@ interface FileService { * otherwise the same as [FileService.create]. */ @MustBeClosed + fun create( + vectorStoreId: String, + params: FileCreateParams, + ): HttpResponseFor = create(vectorStoreId, params, RequestOptions.none()) + + /** @see [create] */ + @MustBeClosed + fun create( + vectorStoreId: String, + params: FileCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + create(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [create] */ + @MustBeClosed fun create(params: FileCreateParams): HttpResponseFor = create(params, RequestOptions.none()) @@ -110,6 +200,20 @@ interface FileService { * but is otherwise the same as [FileService.retrieve]. */ @MustBeClosed + fun retrieve(fileId: String, params: FileRetrieveParams): HttpResponseFor = + retrieve(fileId, params, RequestOptions.none()) + + /** @see [retrieve] */ + @MustBeClosed + fun retrieve( + fileId: String, + params: FileRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [retrieve] */ + @MustBeClosed fun retrieve(params: FileRetrieveParams): HttpResponseFor = retrieve(params, RequestOptions.none()) @@ -125,6 +229,20 @@ interface FileService { * but is otherwise the same as [FileService.update]. */ @MustBeClosed + fun update(fileId: String, params: FileUpdateParams): HttpResponseFor = + update(fileId, params, RequestOptions.none()) + + /** @see [update] */ + @MustBeClosed + fun update( + fileId: String, + params: FileUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + update(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [update] */ + @MustBeClosed fun update(params: FileUpdateParams): HttpResponseFor = update(params, RequestOptions.none()) @@ -140,8 +258,24 @@ interface FileService { * otherwise the same as [FileService.list]. */ @MustBeClosed - fun list(params: FileListParams): HttpResponseFor = - list(params, RequestOptions.none()) + fun list(vectorStoreId: String): HttpResponseFor = + list(vectorStoreId, FileListParams.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + vectorStoreId: String, + params: FileListParams = FileListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + list(params.toBuilder().vectorStoreId(vectorStoreId).build(), requestOptions) + + /** @see [list] */ + @MustBeClosed + fun list( + vectorStoreId: String, + params: FileListParams = FileListParams.none(), + ): HttpResponseFor = list(vectorStoreId, params, RequestOptions.none()) /** @see [list] */ @MustBeClosed @@ -150,12 +284,41 @@ interface FileService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** @see [list] */ + @MustBeClosed + fun list(params: FileListParams): HttpResponseFor = + list(params, RequestOptions.none()) + + /** @see [list] */ + @MustBeClosed + fun list( + vectorStoreId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + list(vectorStoreId, FileListParams.none(), requestOptions) + /** * Returns a raw HTTP response for `delete * /vector_stores/{vector_store_id}/files/{file_id}`, but is otherwise the same as * [FileService.delete]. */ @MustBeClosed + fun delete( + fileId: String, + params: FileDeleteParams, + ): HttpResponseFor = delete(fileId, params, RequestOptions.none()) + + /** @see [delete] */ + @MustBeClosed + fun delete( + fileId: String, + params: FileDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + delete(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [delete] */ + @MustBeClosed fun delete(params: FileDeleteParams): HttpResponseFor = delete(params, RequestOptions.none()) @@ -172,6 +335,20 @@ interface FileService { * [FileService.content]. */ @MustBeClosed + fun content(fileId: String, params: FileContentParams): HttpResponseFor = + content(fileId, params, RequestOptions.none()) + + /** @see [content] */ + @MustBeClosed + fun content( + fileId: String, + params: FileContentParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + content(params.toBuilder().fileId(fileId).build(), requestOptions) + + /** @see [content] */ + @MustBeClosed fun content(params: FileContentParams): HttpResponseFor = content(params, RequestOptions.none()) diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileServiceImpl.kt index c3e9174a..971cf72e 100644 --- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileServiceImpl.kt +++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/vectorstores/FileServiceImpl.kt @@ -4,6 +4,7 @@ package com.openai.services.blocking.vectorstores import com.openai.core.ClientOptions import com.openai.core.RequestOptions +import com.openai.core.checkRequired import com.openai.core.handlers.errorHandler import com.openai.core.handlers.jsonHandler import com.openai.core.handlers.withErrorHandler @@ -28,6 +29,7 @@ import com.openai.models.vectorstores.files.FileRetrieveParams import com.openai.models.vectorstores.files.FileUpdateParams import com.openai.models.vectorstores.files.VectorStoreFile import com.openai.models.vectorstores.files.VectorStoreFileDeleted +import kotlin.jvm.optionals.getOrNull class FileServiceImpl internal constructor(private val clientOptions: ClientOptions) : FileService { @@ -87,6 +89,9 @@ class FileServiceImpl internal constructor(private val clientOptions: ClientOpti params: FileCreateParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("vectorStoreId", params.vectorStoreId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -115,6 +120,9 @@ class FileServiceImpl internal constructor(private val clientOptions: ClientOpti params: FileRetrieveParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fileId", params.fileId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -147,6 +155,9 @@ class FileServiceImpl internal constructor(private val clientOptions: ClientOpti params: FileUpdateParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fileId", params.fileId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) @@ -181,6 +192,9 @@ class FileServiceImpl internal constructor(private val clientOptions: ClientOpti params: FileListParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("vectorStoreId", params.vectorStoreId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) @@ -216,6 +230,9 @@ class FileServiceImpl internal constructor(private val clientOptions: ClientOpti params: FileDeleteParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fileId", params.fileId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.DELETE) @@ -250,6 +267,9 @@ class FileServiceImpl internal constructor(private val clientOptions: ClientOpti params: FileContentParams, requestOptions: RequestOptions, ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("fileId", params.fileId().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.GET) diff --git a/openai-java-core/src/test/kotlin/com/openai/core/AutoPagerAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/core/AutoPagerAsyncTest.kt new file mode 100644 index 00000000..e9e9137e --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/core/AutoPagerAsyncTest.kt @@ -0,0 +1,182 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.core + +import com.openai.core.http.AsyncStreamResponse +import java.util.Optional +import java.util.concurrent.CompletableFuture +import java.util.concurrent.Executor +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.catchThrowable +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any +import org.mockito.kotlin.clearInvocations +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.inOrder +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.spy +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@ExtendWith(MockitoExtension::class) +internal class AutoPagerAsyncTest { + + companion object { + + private val ERROR = RuntimeException("ERROR!") + } + + private class PageAsyncImpl( + private val items: List, + private val hasNext: Boolean = true, + ) : PageAsync { + + val nextPageFuture: CompletableFuture> = CompletableFuture() + + override fun hasNextPage(): Boolean = hasNext + + override fun nextPage(): CompletableFuture> = nextPageFuture + + override fun items(): List = items + } + + private val executor = + spy { + doAnswer { invocation -> invocation.getArgument(0).run() } + .whenever(it) + .execute(any()) + } + private val handler = mock>() + + @Test + fun subscribe_whenAlreadySubscribed_throws() { + val autoPagerAsync = AutoPagerAsync.from(PageAsyncImpl(emptyList()), executor) + autoPagerAsync.subscribe {} + clearInvocations(executor) + + val throwable = catchThrowable { autoPagerAsync.subscribe {} } + + assertThat(throwable).isInstanceOf(IllegalStateException::class.java) + assertThat(throwable).hasMessage("Cannot subscribe more than once") + verify(executor, never()).execute(any()) + } + + @Test + fun subscribe_whenClosed_throws() { + val autoPagerAsync = AutoPagerAsync.from(PageAsyncImpl(emptyList()), executor) + autoPagerAsync.close() + + val throwable = catchThrowable { autoPagerAsync.subscribe {} } + + assertThat(throwable).isInstanceOf(IllegalStateException::class.java) + assertThat(throwable).hasMessage("Cannot subscribe after the response is closed") + verify(executor, never()).execute(any()) + } + + @Test + fun subscribe_whenFirstPageNonEmpty_runsHandler() { + val page = PageAsyncImpl(listOf("item1", "item2", "item3"), hasNext = false) + val autoPagerAsync = AutoPagerAsync.from(page, executor) + + autoPagerAsync.subscribe(handler) + + inOrder(executor, handler) { + verify(executor, times(1)).execute(any()) + verify(handler, times(1)).onNext("item1") + verify(handler, times(1)).onNext("item2") + verify(handler, times(1)).onNext("item3") + verify(handler, times(1)).onComplete(Optional.empty()) + } + } + + @Test + fun subscribe_whenFutureCompletesAfterClose_doesNothing() { + val page = PageAsyncImpl(listOf("page1")) + val autoPagerAsync = AutoPagerAsync.from(page, executor) + autoPagerAsync.subscribe(handler) + autoPagerAsync.close() + + page.nextPageFuture.complete(PageAsyncImpl(listOf("page2"))) + + verify(handler, times(1)).onNext("page1") + verify(handler, never()).onNext("page2") + verify(handler, times(1)).onComplete(Optional.empty()) + verify(executor, times(1)).execute(any()) + } + + @Test + fun subscribe_whenFutureErrors_callsOnComplete() { + val page = PageAsyncImpl(emptyList()) + val autoPagerAsync = AutoPagerAsync.from(page, executor) + autoPagerAsync.subscribe(handler) + + page.nextPageFuture.completeExceptionally(ERROR) + + verify(executor, times(1)).execute(any()) + verify(handler, never()).onNext(any()) + verify(handler, times(1)).onComplete(Optional.of(ERROR)) + } + + @Test + fun subscribe_whenFutureCompletes_runsHandler() { + val page = PageAsyncImpl(listOf("chunk1", "chunk2")) + val autoPagerAsync = AutoPagerAsync.from(page, executor) + + autoPagerAsync.subscribe(handler) + + verify(handler, never()).onComplete(any()) + inOrder(executor, handler) { + verify(executor, times(1)).execute(any()) + verify(handler, times(1)).onNext("chunk1") + verify(handler, times(1)).onNext("chunk2") + } + clearInvocations(executor, handler) + + page.nextPageFuture.complete(PageAsyncImpl(listOf("chunk3", "chunk4"), hasNext = false)) + + verify(executor, never()).execute(any()) + inOrder(handler) { + verify(handler, times(1)).onNext("chunk3") + verify(handler, times(1)).onNext("chunk4") + verify(handler, times(1)).onComplete(Optional.empty()) + } + } + + @Test + fun onCompleteFuture_whenNextPageFutureNotCompleted_onCompleteFutureNotCompleted() { + val page = PageAsyncImpl(listOf("chunk1", "chunk2")) + val autoPagerAsync = AutoPagerAsync.from(page, executor) + autoPagerAsync.subscribe {} + + val onCompletableFuture = autoPagerAsync.onCompleteFuture() + + assertThat(onCompletableFuture).isNotCompleted + } + + @Test + fun onCompleteFuture_whenNextPageFutureErrors_onCompleteFutureCompletedExceptionally() { + val page = PageAsyncImpl(listOf("chunk1", "chunk2")) + val autoPagerAsync = AutoPagerAsync.from(page, executor) + autoPagerAsync.subscribe {} + page.nextPageFuture.completeExceptionally(ERROR) + + val onCompletableFuture = autoPagerAsync.onCompleteFuture() + + assertThat(onCompletableFuture).isCompletedExceptionally + } + + @Test + fun onCompleteFuture_whenNoNextPage_onCompleteFutureCompleted() { + val page = PageAsyncImpl(listOf("chunk1", "chunk2"), hasNext = false) + val autoPagerAsync = AutoPagerAsync.from(page, executor) + autoPagerAsync.subscribe {} + + val onCompletableFuture = autoPagerAsync.onCompleteFuture() + + assertThat(onCompletableFuture).isCompleted + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/core/AutoPagerTest.kt b/openai-java-core/src/test/kotlin/com/openai/core/AutoPagerTest.kt new file mode 100644 index 00000000..17e58632 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/core/AutoPagerTest.kt @@ -0,0 +1,41 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.core + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class AutoPagerTest { + + private class PageImpl( + private val items: List, + private val nextPage: Page? = null, + ) : Page { + + override fun hasNextPage(): Boolean = nextPage != null + + override fun nextPage(): Page = nextPage!! + + override fun items(): List = items + } + + @Test + fun iterator() { + val firstPage = + PageImpl(listOf("chunk1", "chunk2"), nextPage = PageImpl(listOf("chunk3", "chunk4"))) + + val autoPager = AutoPager.from(firstPage) + + assertThat(autoPager).containsExactly("chunk1", "chunk2", "chunk3", "chunk4") + } + + @Test + fun stream() { + val firstPage = + PageImpl(listOf("chunk1", "chunk2"), nextPage = PageImpl(listOf("chunk3", "chunk4"))) + + val autoPager = AutoPager.from(firstPage) + + assertThat(autoPager.stream()).containsExactly("chunk1", "chunk2", "chunk3", "chunk4") + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTest.kt b/openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTest.kt new file mode 100644 index 00000000..dd5cdd57 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTest.kt @@ -0,0 +1,1520 @@ +package com.openai.core + +import com.fasterxml.jackson.annotation.JsonClassDescription +import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonPropertyDescription +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.node.ObjectNode +import com.openai.errors.OpenAIInvalidDataException +import java.util.Optional +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatNoException +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.AfterTestExecutionCallback +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.RegisterExtension + +/** Tests for the `StructuredOutputs` functions and the [JsonSchemaValidator]. */ +internal class StructuredOutputsTest { + companion object { + private const val SCHEMA = "\$schema" + private const val SCHEMA_VER = "https://json-schema.org/draft/2020-12/schema" + private const val DEFS = "\$defs" + private const val REF = "\$ref" + + /** + * `true` to print the schema and validation errors for all executed tests, or `false` to + * print them only for failed tests. + */ + private const val VERBOSE_MODE = false + + private fun parseJson(schemaString: String) = ObjectMapper().readTree(schemaString) + } + + /** + * A validator that can be used by each unit test. A new validation instance is created for each + * test, as each test is run from its own instance of the test class. If a test fails, any + * validation errors are automatically printed to standard output to aid diagnosis. + */ + val validator = JsonSchemaValidator.create() + + /** + * The schema that was created by the unit test. This may be printed out after a test fails to + * aid in diagnosing the cause of the failure. In that case, this property must be set, or an + * error will occur. However, it will only be printed if the failed test method has the name + * prefix `schemaTest_`, so only test methods with that naming pattern need to set this field. + */ + lateinit var schema: JsonNode + + /** + * An extension to JUnit that prints the [schema] and the validation status (including any + * errors) when a test fails. This applies only to test methods whose names are prefixed with + * `schemaTest_`. An error will occur if [schema] was not set, but this can be avoided by only + * using the method name prefix for test methods that set [schema]. This reporting is intended + * as an aid to diagnosing test failures. + */ + @Suppress("unused") + @RegisterExtension + val printValidationErrorsOnFailure: AfterTestExecutionCallback = + object : AfterTestExecutionCallback { + @Throws(Exception::class) + override fun afterTestExecution(context: ExtensionContext) { + if ( + context.displayName.startsWith("schemaTest_") && + (VERBOSE_MODE || context.executionException.isPresent) + ) { + // Test failed. + println("Schema: ${schema.toPrettyString()}\n") + println("$validator\n") + } + } + } + + // NOTE: In most of these tests, it is assumed that the schema is generated as expected; it is + // not examined in fine detail if the validator succeeds or fails with the expected errors. + + @Test + fun schemaTest_minimalSchema() { + class X() + + schema = extractSchema(X::class.java) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_minimalListSchema() { + val s: List = listOf() + + schema = extractSchema(s.javaClass) + validator.validate(schema) + + // Currently, the generated schema looks like this: + // + // { + // "$schema" : "https://json-schema.org/draft/2020-12/schema", + // "type" : "array", + // "items" : { } + // } + // + // That causes an error, as the `"items"` object is empty when it should be a valid + // sub-schema. Something like this is what would be valid: + // + // { + // "$schema" : "https://json-schema.org/draft/2020-12/schema", + // "type" : "array", + // "items" : { + // "type" : "string" + // } + // } + // + // The reason for the failure is that generic type information is erased for scopes like + // local variables, but generic type information for fields is retained as part of the class + // metadata. This is the expected behavior in Java, so this test expects an invalid schema. + assertThat(validator.isValid()).isFalse + assertThat(validator.errors()).hasSize(2) + assertThat(validator.errors()[0]).isEqualTo("#/items: Schema or sub-schema is empty.") + assertThat(validator.errors()[1]) + .isEqualTo("#/items: Expected exactly one of 'type' or 'anyOf' or '$REF'.") + } + + @Test + fun schemaTest_listFieldSchema() { + @Suppress("unused") class X(val s: List) + + schema = extractSchema(X::class.java) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + // This gives a root schema with `"type" : "string"` and `"const" : "HELLO"` + // Unfortunately, an "enum class" cannot be defined within a function or within a class within + // a function. + @Suppress("unused") + enum class MinimalEnum1 { + HELLO + } + + @Test + fun schemaTest_minimalEnumSchema1() { + schema = extractSchema(MinimalEnum1::class.java) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + // This gives a root schema with `"type" : "string"` and `"enum" : [ "HELLO", "WORLD" ]` + @Suppress("unused") + enum class MinimalEnum2 { + HELLO, + WORLD, + } + + @Test + fun schemaTest_minimalEnumSchema2() { + schema = extractSchema(MinimalEnum2::class.java) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_nonStringEnum() { + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "integer", + "enum" : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_tinySchema() { + @Suppress("unused") class X(val s: String) + + schema = extractSchema(X::class.java) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_tinySchemaFromOptionalString() { + // Using an `Optional` will result in this JSON: `"type" : [ "string", "null" ]`. + // That is supported by the OpenAI Structured Outputs API spec, as long as the field is also + // marked as required. Though required, it is still allowed for the field to be explicitly + // set to `"null"`. + @Suppress("unused") class X(val s: Optional) + + schema = extractSchema(X::class.java) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_tinySchemaFromOptionalBoolean() { + @Suppress("unused") class X(val b: Optional) + + schema = extractSchema(X::class.java) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_tinySchemaFromOptionalInteger() { + @Suppress("unused") class X(val i: Optional) + + schema = extractSchema(X::class.java) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_tinySchemaFromOptionalNumber() { + @Suppress("unused") class X(val n: Optional) + + schema = extractSchema(X::class.java) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_arraySchemaFromOptional() { + @Suppress("unused") class X(val s: Optional>) + + schema = extractSchema(X::class.java) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_arrayTypeMissingItems() { + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "array" + } + """ + ) + validator.validate(schema) + + // Check once here that "validator.isValid()" returns "false" when there is an error. In + // the other tests, there is no need to repeat this assertion, as it would be redundant. + assertThat(validator.isValid()).isFalse + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#: 'items' field is missing or is not an object.") + } + + @Test + fun schemaTest_arrayTypeWithWrongItemsType() { + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "array", + "items" : [ "should_not_be_an_array" ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#: 'items' field is missing or is not an object.") + } + + @Test + @Suppress("unused") + fun schemaTest_objectSubSchemaFromOptional() { + class X(val s: Optional) + class Y(val x: Optional) + + schema = extractSchema(Y::class.java) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_badOptionalTypeNotArray() { + // Testing more for code coverage than for anything expected to go wrong in practice. + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : { "type" : "string" } + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#: 'type' field is not a type name or array of type names.") + } + + @Test + fun schemaTest_badOptionalTypeNoNull1() { + // Testing more for code coverage than for anything expected to go wrong in practice. + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : [ "string" ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#/type: Expected exactly two types, both strings.") + } + + @Test + fun schemaTest_badOptionalTypeNoNull2() { + // If "type" is an array, one of the two "type" values must be "null". + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : [ "string", "number" ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#/type: Expected one type name and one \"null\".") + } + + @Test + fun schemaTest_badOptionalTypeNoNull3() { + // If "type" is an array, there must be two type values only, one of them "null". + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : [ "string", "number", "null" ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#/type: Expected exactly two types, both strings.") + } + + @Test + fun schemaTest_badOptionalTypeNoStringTypeNames() { + // If "type" is an array, there must be two type values only, one of them "null". + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : [ "string", null ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#/type: Expected exactly two types, both strings.") + } + + @Test + fun schemaTest_badOptionalTypeAllNull() { + // If "type" is an array, there must be two type values only, and only one of them "null". + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : [ "null", "null" ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#/type: Expected one type name and one \"null\".") + } + + @Test + fun schemaTest_badOptionalTypeUnknown() { + // If "type" is an array, there must be two type values only, and only one of them "null". + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : [ "unknown", "null" ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]).isEqualTo("#/type: Unsupported 'type' value: 'unknown'.") + } + + @Test + fun schemaTest_goodOptionalTypeNullFirst() { + // The validator should be lenient about the order of the null/not-null types in the array. + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : [ "null", "string" ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_tinyRecursiveSchema() { + @Suppress("unused") class X(val s: String, val x: X?) + + schema = extractSchema(X::class.java) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_unsupportedKeywords() { + // OpenAI lists a set of keywords that are not allowed, but the set is not exhaustive. Check + // that everything named in that set is identified as not allowed, as that is the minimum + // level of validation expected. Check at the root schema and a sub-schema. There is no need + // to match the keywords to their expected schema types or be concerned about the values of + // the keyword fields, which makes testing easier. + val keywordsNotAllowed = + listOf( + "minLength", + "maxLength", + "pattern", + "format", + "minimum", + "maximum", + "multipleOf", + "patternProperties", + "unevaluatedProperties", + "propertyNames", + "minProperties", + "maxProperties", + "unevaluatedItems", + "contains", + "minContains", + "maxContains", + "minItems", + "maxItems", + "uniqueItems", + ) + val notAllowedUses = keywordsNotAllowed.joinToString(", ") { "\"$it\" : \"\"" } + + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "properties" : { + "x" : { + "type" : "string", + $notAllowedUses + } + }, + $notAllowedUses, + "additionalProperties" : false, + "required" : [ "x" ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(keywordsNotAllowed.size * 2) + keywordsNotAllowed.forEachIndexed { index, keyword -> + assertThat(validator.errors()[index]) + .isEqualTo("#: Use of '$keyword' is not supported here.") + assertThat(validator.errors()[index + keywordsNotAllowed.size]) + .isEqualTo("#/properties/x: Use of '$keyword' is not supported here.") + } + } + + @Test + fun schemaTest_propertyNotMarkedRequired() { + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "properties" : { "name" : { "type" : "string" } }, + "additionalProperties" : false, + "required" : [ ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#/required: 'properties' field 'name' is not listed as 'required'.") + } + + @Test + fun schemaTest_requiredArrayNull() { + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "properties" : { "name" : { "type" : "string" } }, + "additionalProperties" : false, + "required" : null + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#/required: 'properties' field 'name' is not listed as 'required'.") + } + + @Test + fun schemaTest_requiredArrayMissing() { + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "properties" : { "name" : { "type" : "string" } }, + "additionalProperties" : false + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#/required: 'properties' field 'name' is not listed as 'required'.") + } + + @Test + fun schemaTest_additionalPropertiesMissing() { + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "properties" : { "name" : { "type" : "string" } }, + "required" : [ "name" ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#: 'additionalProperties' field is missing or is not set to 'false'.") + } + + @Test + fun schemaTest_additionalPropertiesTrue() { + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "properties" : { "name" : { "type" : "string" } }, + "additionalProperties" : true, + "required" : [ "name" ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#: 'additionalProperties' field is missing or is not set to 'false'.") + } + + @Test + fun schemaTest_objectPropertiesMissing() { + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "additionalProperties" : false, + "required" : [ ] + } + """ + ) + validator.validate(schema) + + // For now, allow that an object may have no properties. Update this if that is revised. + assertThat(validator.isValid()).isTrue() + } + + @Test + fun schemaTest_objectPropertiesNotObject() { + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "properties" : [ "name", "age" ], + "additionalProperties" : false, + "required" : [ "name", "age" ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#: 'properties' field is not a non-empty object.") + } + + @Test + fun schemaTest_objectPropertiesEmpty() { + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "properties" : { }, + "additionalProperties" : false, + "required" : [ ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#: 'properties' field is not a non-empty object.") + } + + @Test + fun schemaTest_anyOfInRootSchema() { + // OpenAI does not allow `"anyOf"` to appear at the root level of a schema. + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "anyOf" : [ { + "type" : "object", + "properties" : { "name" : { "type" : "string" } }, + "additionalProperties" : false, + "required" : ["name"] + }, { + "type" : "array", + "items" : { + "type" : "object", + "properties" : { "name" : { "type" : "string" } }, + "additionalProperties" : false, + "required" : ["name"] + } + } ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]).isEqualTo("#: Root schema contains 'anyOf' field.") + } + + @Test + fun schemaTest_anyOfNotArray() { + // Unlikely that this can occur in a generated schema, so this is more about code coverage. + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "properties" : { + "name" : { + "anyOf" : { + "type" : "string" + } + } + }, + "additionalProperties" : false, + "required" : ["name"] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#/properties/name: 'anyOf' field is not a non-empty array.") + } + + @Test + fun schemaTest_anyOfIsEmptyArray() { + // Unlikely that this can occur in a generated schema, so this is more about code coverage. + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "properties" : { + "name" : { + "anyOf" : [ ] + } + }, + "additionalProperties" : false, + "required" : ["name"] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#/properties/name: 'anyOf' field is not a non-empty array.") + } + + @Test + fun schemaTest_anyOfInSubSchemaArray() { + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "properties" : { + "value" : { + "anyOf" : [ + { "type" : "string" }, + { "type" : "number" } + ] + } + }, + "additionalProperties" : false, + "required" : ["value"] + } + """ + ) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_noSchemaFieldRootSchema() { + @Suppress("unused") class X(val s: String) + + schema = extractSchema(X::class.java) + (schema as ObjectNode).remove(SCHEMA) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]).isEqualTo("#: Root schema missing '$SCHEMA' field.") + } + + @Test + @Suppress("unused") + fun schemaTest_deepNestingAtLimit() { + class U(val s: String) + class V(val u: U) + class W(val v: V) + class X(val w: W) + class Y(val x: X) + + schema = extractSchema(Y::class.java) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + @Suppress("unused") + fun schemaTest_deepNestingBeyondLimit() { + class U(val s: String) + class V(val u: U) + class W(val v: V) + class X(val w: W) + class Y(val x: X) + class Z(val y: Y) + + schema = extractSchema(Z::class.java) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]).contains("Current nesting depth is 6, but maximum is 5.") + } + + @Test + fun schemaTest_stringEnum250ValueOverSizeLimit() { + // OpenAI specification: "For a single enum property with string values, the total string + // length of all enum values cannot exceed 7,500 characters when there are more than 250 + // enum values." + + // This test creates an enum with exactly 250 string values with more than 7,500 characters + // in total (31 characters per value for a total of 7,750 characters). No error is expected. + val values = (1..250).joinToString(", ") { "\"%s%03d\"".format("x".repeat(28), it) } + + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "string", + "enum" : [ $values ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_stringEnum251ValueUnderSizeLimit() { + // This test creates an enum with exactly 251 string values with fewer than 7,500 characters + // in total (29 characters per value for a total of 7,279 characters). No error is expected. + val values = (1..251).joinToString(", ") { "\"%s%03d\"".format("x".repeat(26), it) } + + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "string", + "enum" : [ $values ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_stringEnum251ValueOverSizeLimit() { + // This test creates an enum with exactly 251 string values with fewer than 7,500 characters + // in total (30 characters per value for a total of 7,530 characters). An error is expected. + val values = (1..251).joinToString(", ") { "\"%s%03d\"".format("x".repeat(27), it) } + + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "string", + "enum" : [ $values ] + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo( + "#/enum: Total string length (7530) of values of an enum " + + "with 251 values exceeds limit of 7500." + ) + } + + @Test + fun schemaTest_totalEnumValuesAtLimit() { + // OpenAI specification: "A schema may have up to 500 enum values across all enum + // properties." + + // This test creates two enums with a total of 500 values. The total string length of the + // values is well within the limits (2,000 characters). + val valuesA = (1..250).joinToString(", ") { "\"a%03d\"".format(it) } + val valuesB = (1..250).joinToString(", ") { "\"b%03d\"".format(it) } + + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "properties" : { + "a" : { + "type" : "string", + "enum" : [ $valuesA ] + }, + "b" : { + "type" : "string", + "enum" : [ $valuesB ] + } + }, + "required" : [ "a", "b" ], + "additionalProperties" : false + } + """ + ) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_totalEnumValuesOverLimit() { + // This test creates two enums with a total of 501 values. The total string length of the + // values is well within the limits (2,004 characters). + val valuesA = (1..250).joinToString(", ") { "\"a%03d\"".format(it) } + val valuesB = (1..251).joinToString(", ") { "\"b%03d\"".format(it) } + + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "properties" : { + "a" : { + "type" : "string", + "enum" : [ $valuesA ] + }, + "b" : { + "type" : "string", + "enum" : [ $valuesB ] + } + }, + "required" : [ "a", "b" ], + "additionalProperties" : false + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#: Total number of enum values (501) exceeds limit of 500.") + } + + @Test + fun schemaTest_maxObjectPropertiesAtLimit() { + // This test creates two object schemas with a total of 100 object properties. OpenAI does + // not support more than 100 properties total in the whole schema. Two objects are used to + // ensure that counting is not done per object, but across all objects. Note that each + // object schema is itself a property, so there are two properties at the top level and 49 + // properties each at the next level. No error is expected, as the limit is not exceeded. + val propUses = + (1..49).joinToString(", ") { "\"x%02d\" : { \"type\" : \"string\" }".format(it) } + val propNames = (1..49).joinToString(", ") { "\"x%02d\"".format(it) } + + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "properties" : { + "a" : { + "type" : "object", + "properties" : { + $propUses + }, + "required" : [ $propNames ], + "additionalProperties" : false + }, + "b" : { + "type" : "object", + "properties" : { + $propUses + }, + "required" : [ $propNames ], + "additionalProperties" : false + } + }, + "required" : [ "a", "b" ], + "additionalProperties" : false + } + """ + ) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_maxObjectPropertiesOverLimit() { + // This test creates two object schemas with a total of 101 object properties. OpenAI does + // not support more than 100 properties total in the whole schema. Expect an error. + val propUses = + (1..49).joinToString(", ") { "\"x_%02d\" : { \"type\" : \"string\" }".format(it) } + val propNames = (1..49).joinToString(", ") { "\"x_%02d\"".format(it) } + + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "type" : "object", + "properties" : { + "a" : { + "type" : "object", + "properties" : { + $propUses + }, + "required" : [ $propNames ], + "additionalProperties" : false + }, + "b" : { + "type" : "object", + "properties" : { + $propUses, + "property_101" : { "type" : "string" } + }, + "required" : [ $propNames, "property_101" ], + "additionalProperties" : false + } + }, + "required" : [ "a", "b" ], + "additionalProperties" : false + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#: Total number of object properties (101) exceeds limit of 100.") + } + + @Test + fun schemaTest_maxStringLengthAtLimit() { + // OpenAI specification: "In a schema, total string length of all property names, definition + // names, enum values, and const values cannot exceed 15,000 characters." + // + // This test creates a schema with many property names, definition names, enum values, and + // const values calculated to have a total string length of 15,000 characters. No error is + // expected. + // + // The test creates a schema that looks like the following, with the numbers adjusted to + // achieve a total of 15,000 characters for the relevant elements. + // + // { + // "$schema" : "...", + // "$defs" : { + // "d_001" : { + // "type" : "string", + // "const" : "c_001" + // }, + // ..., + // "d_nnn" : { + // "type" : "string", + // "const" : "c_nnn" + // } + // }, + // "type" : "object", + // "properties" : { + // "p_001" : { + // "type" : "string", + // "enum" : [ "eeeee..._001", ..., "eeeee..._nnn" ] + // }, + // ..., + // "p_nnn" : { + // "type" : "string", + // "enum" : [ "eeeee..._001", ..., "eeeee..._nnn" ] + // } + // }, + // "required" : [ "p_001", ..., "p_nnn" ], + // "additionalProperties" : false + // } + + val numDefs = 65 // Each also has one "const" value. + val numProps = 70 // Each also has "numEnumValues" enum values. + val nameLen = 5 // Length of names of definitions, properties and const values. + val numEnumValues = 5 // numProps * numEnumValues <= 500 limit (OpenAI) + val enumValueLen = 40 // Length of enum values. + val expectedTotalStringLength = + nameLen * (numProps + numDefs * 2) + numProps * enumValueLen * numEnumValues + + val enumValues = + (1..numEnumValues).joinToString(", ") { "\"%s_%03d\"".format("e".repeat(36), it) } + val defs = + (1..numDefs).joinToString(", ") { + "\"d_%03d\" : { \"type\" : \"string\", \"const\" : \"c_%03d\" }".format(it, it) + } + val props = + (1..numProps).joinToString(", ") { + "\"p_%03d\" : { \"type\" : \"string\", \"enum\" : [ $enumValues ] }".format(it) + } + val propNames = (1..numProps).joinToString(", ") { "\"p_%03d\"".format(it) } + + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "$DEFS" : { $defs }, + "type" : "object", + "properties" : { $props }, + "required" : [ $propNames ], + "additionalProperties" : false + } + """ + ) + validator.validate(schema) + + assertThat(expectedTotalStringLength).isEqualTo(15_000) // Exactly on the limit. + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_maxStringLengthOverLimit() { + // OpenAI specification: "In a schema, total string length of all property names, definition + // names, enum values, and const values cannot exceed 15,000 characters." + // + // This test creates a schema with many property names, definition names, enum values, and + // const values calculated to have a total string length of just over 15,000 characters. An + // error is expected. + + val numDefs = 66 // Each also has one "const" value. + val numProps = 70 // Each also has "numEnumValues" enum values. + val numEnumValues = 5 // numProps * numEnumValues <= 500 limit (OpenAI) + val nameLen = 5 // Length of names of definitions, properties and const values. + val enumValueLen = 40 // Length of enum values. + val expectedTotalStringLength = + nameLen * (numProps + numDefs * 2) + numProps * enumValueLen * numEnumValues + + val enumValues = + (1..numEnumValues).joinToString(", ") { "\"%s_%03d\"".format("e".repeat(36), it) } + val defs = + (1..numDefs).joinToString(", ") { + "\"d_%03d\" : { \"type\" : \"string\", \"const\" : \"c_%03d\" }".format(it, it) + } + val props = + (1..numProps).joinToString(", ") { + "\"p_%03d\" : { \"type\" : \"string\", \"enum\" : [ $enumValues ] }".format(it) + } + val propNames = (1..numProps).joinToString(", ") { "\"p_%03d\"".format(it) } + + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "$DEFS" : { $defs }, + "type" : "object", + "properties" : { $props }, + "required" : [ $propNames ], + "additionalProperties" : false + } + """ + ) + validator.validate(schema) + + assertThat(expectedTotalStringLength).isGreaterThan(15_000) + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#: Total string length of all values (15010) exceeds limit of 15000.") + } + + @Test + fun schemaTest_annotatedWithJsonClassDescription() { + // Add a "description" to the root schema using an annotation. + @JsonClassDescription("A simple schema.") class X() + + schema = extractSchema(X::class.java) + validator.validate(schema) + + // Assume that the schema is well-formed. + val desc = schema.get("description") + + assertThat(validator.isValid()).isTrue + assertThat(desc).isNotNull + assertThat(desc.isTextual).isTrue + assertThat(desc.asText()).isEqualTo("A simple schema.") + } + + @Test + fun schemaTest_annotatedWithJsonPropertyDescription() { + // Add a "description" to the property using an annotation. + @Suppress("unused") class X(@get:JsonPropertyDescription("A string value.") val s: String) + + schema = extractSchema(X::class.java) + validator.validate(schema) + + // Assume that the schema is well-formed. + val properties = schema.get("properties") + val stringProperty = properties.get("s") + val desc = stringProperty.get("description") + + assertThat(validator.isValid()).isTrue + assertThat(desc).isNotNull + assertThat(desc.isTextual).isTrue + assertThat(desc.asText()).isEqualTo("A string value.") + } + + @Test + fun schemaTest_annotatedWithJsonProperty() { + // Override the default name of the property using the annotation. + @Suppress("unused") class X(@get:JsonProperty("a_string") val s: String) + + schema = extractSchema(X::class.java) + validator.validate(schema) + + // Assume that the schema is well-formed. + val properties = schema.get("properties") + val stringProperty = properties.get("a_string") + + assertThat(validator.isValid()).isTrue + assertThat(stringProperty).isNotNull + } + + @Test + fun schemaTest_annotatedWithJsonPropertyRejectDefaultValue() { + // Set a default value for the property. It should be ignored when the schema is generated, + // as default property values are not supported in OpenAI JSON schemas. (The Victools docs + // have examples of how to add support for this default values via annotations or initial + // values, should support for default values be needed in the future.) + // + // Lack of support is not mentioned in the specification, but see the evidence at: + // https://engineering.fractional.ai/openai-structured-output-fixes + @Suppress("unused") + class X( + @get:JsonProperty(defaultValue = "default_value_1") val s: String = "default_value_2" + ) + + schema = extractSchema(X::class.java) + validator.validate(schema) + + // Assume that the schema is well-formed. + val properties = schema.get("properties") + val stringProperty = properties.get("s") + + assertThat(validator.isValid()).isTrue + assertThat(stringProperty).isNotNull + assertThat(stringProperty.get("default")).isNull() + } + + @Test + fun schemaTest_annotatedWithJsonIgnore() { + // Override the default name of the property using the annotation. + @Suppress("unused") class X(@get:JsonIgnore val s1: String, val s2: String) + + schema = extractSchema(X::class.java) + validator.validate(schema) + + // Assume that the schema is well-formed. + val properties = schema.get("properties") + val s1Property = properties.get("s1") + val s2Property = properties.get("s2") + + assertThat(validator.isValid()).isTrue + assertThat(s1Property).isNull() + assertThat(s2Property).isNotNull + } + + @Test + fun schemaTest_emptyDefinitions() { + // Be lenient about empty definitions. + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "$DEFS" : { }, + "type" : "string" + } + """ + ) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun schemaTest_referenceMissingReferent() { + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "$DEFS" : { }, + "$REF" : "#/$DEFS/Person" + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]) + .isEqualTo("#/$REF: Invalid or unsupported reference: '#/$DEFS/Person'.") + } + + @Test + fun schemaTest_referenceFieldIsNotTextual() { + schema = + parseJson( + """ + { + "$SCHEMA" : "$SCHEMA_VER", + "$DEFS" : { }, + "$REF" : 42 + } + """ + ) + validator.validate(schema) + + assertThat(validator.errors()).hasSize(1) + assertThat(validator.errors()[0]).isEqualTo("#/$REF: '$REF' field is not a text value.") + } + + @Test + fun validatorBeforeValidation() { + assertThat(validator.errors()).isEmpty() + assertThat(validator.isValid()).isFalse + } + + @Test + fun validatorReused() { + class X() + + schema = extractSchema(X::class.java) + validator.validate(schema) + + // Should fail if an attempt is made to reuse the validator. + assertThatThrownBy { validator.validate(schema) } + .isExactlyInstanceOf(IllegalStateException::class.java) + .hasMessageContaining("Validation already complete.") + } + + @Test + @Suppress("unused") + fun schemaTest_largeLaureatesSchema() { + // This covers many cases: large and complex "$defs", resolution of references, recursive + // references, etc. The output is assumed to be good (it has been checked by eye) and the + // test just shows that the validator can handle the complexity without crashing or emitting + // spurious errors. + class Name(val givenName: String, val familyName: String) + + class Person( + @get:JsonPropertyDescription("The name of the person.") val name: Name, + @get:JsonProperty(value = "date_of_birth", defaultValue = "unknown_1") + @get:JsonPropertyDescription("The date of birth of the person.") + var dateOfBirth: String, + @get:JsonPropertyDescription("The country of citizenship of the person.") + var nationality: String, + // A child being a `Person` results in a recursive schema. + @get:JsonPropertyDescription("The children (if any) of the person.") + val children: List, + ) { + @get:JsonPropertyDescription("The other name of the person.") + var otherName: Name = Name("Bob", "Smith") + } + + class Laureate( + val laureate: Person, + val majorContribution: String, + val yearOfWinning: String, + @get:JsonIgnore val favoriteColor: String, + ) + + class Laureates( + // Two lists results in a `Laureate` definition that is referenced in the schema. + var laureates1901to1950: List, + var laureates1951to2025: List, + ) + + schema = extractSchema(Laureates::class.java) + validator.validate(schema) + + assertThat(validator.isValid()).isTrue + } + + @Test + fun fromJsonSuccess() { + @Suppress("unused") class X(val s: String) + + val x = responseTypeFromJson("{\"s\" : \"hello\"}", X::class.java) + + assertThat(x.s).isEqualTo("hello") + } + + @Test + fun fromJsonFailure1() { + @Suppress("unused") class X(val s: String) + + // Well-formed JSON, but it does not match the schema of class `X`. + assertThatThrownBy { responseTypeFromJson("{\"wrong\" : \"hello\"}", X::class.java) } + .isExactlyInstanceOf(OpenAIInvalidDataException::class.java) + .hasMessage("Error parsing JSON: {\"wrong\" : \"hello\"}") + } + + @Test + fun fromJsonFailure2() { + @Suppress("unused") class X(val s: String) + + // Malformed JSON. + assertThatThrownBy { responseTypeFromJson("{\"truncated", X::class.java) } + .isExactlyInstanceOf(OpenAIInvalidDataException::class.java) + .hasMessage("Error parsing JSON: {\"truncated") + } + + @Test + fun fromClassEnablesStrictAdherenceToSchema() { + @Suppress("unused") class X(val s: String) + + val jsonSchema = responseFormatFromClass(X::class.java) + + // The "strict" flag _must_ be set to ensure that the model's output will _always_ conform + // to the JSON schema. + assertThat(jsonSchema.jsonSchema().strict()).isPresent + assertThat(jsonSchema.jsonSchema().strict().get()).isTrue + } + + @Test + @Suppress("unused") + fun fromClassSuccessWithoutValidation() { + // Exceed the maximum nesting depth, but do not enable validation. + class U(val s: String) + class V(val u: U) + class W(val v: V) + class X(val w: W) + class Y(val x: X) + class Z(val y: Y) + + assertThatNoException().isThrownBy { + responseFormatFromClass(Z::class.java, JsonSchemaLocalValidation.NO) + } + } + + @Test + fun fromClassSuccessWithValidation() { + @Suppress("unused") class X(val s: String) + + assertThatNoException().isThrownBy { + responseFormatFromClass(X::class.java, JsonSchemaLocalValidation.YES) + } + } + + @Test + @Suppress("unused") + fun fromClassFailureWithValidation() { + // Exceed the maximum nesting depth and enable validation. + class U(val s: String) + class V(val u: U) + class W(val v: V) + class X(val w: W) + class Y(val x: X) + class Z(val y: Y) + + assertThatThrownBy { responseFormatFromClass(Z::class.java, JsonSchemaLocalValidation.YES) } + .isExactlyInstanceOf(IllegalArgumentException::class.java) + .hasMessage( + "Local validation failed for JSON schema derived from ${Z::class.java}:\n" + + " - #/properties/y/properties/x/properties/w/properties/v/properties/u" + + "/properties/s: Current nesting depth is 6, but maximum is 5." + ) + } + + @Test + @Suppress("unused") + fun fromClassFailureWithValidationDefault() { + // Confirm that the default value of the `localValidation` argument is `true` by expecting + // a validation error when that argument is not given an explicit value. + class U(val s: String) + class V(val u: U) + class W(val v: V) + class X(val w: W) + class Y(val x: X) + class Z(val y: Y) + + // Use default for `localValidation` flag. + assertThatThrownBy { responseFormatFromClass(Z::class.java) } + .isExactlyInstanceOf(IllegalArgumentException::class.java) + .hasMessage( + "Local validation failed for JSON schema derived from ${Z::class.java}:\n" + + " - #/properties/y/properties/x/properties/w/properties/v/properties/u" + + "/properties/s: Current nesting depth is 6, but maximum is 5." + ) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTestUtils.kt b/openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTestUtils.kt new file mode 100644 index 00000000..c62eb8ed --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTestUtils.kt @@ -0,0 +1,366 @@ +package com.openai.core + +import java.lang.reflect.Method +import java.util.Optional +import kotlin.reflect.KClass +import kotlin.reflect.KFunction +import kotlin.reflect.KVisibility +import kotlin.reflect.full.declaredFunctions +import kotlin.reflect.jvm.javaMethod +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.fail +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.Mockito.`when` +import org.mockito.kotlin.times +import org.mockito.kotlin.verify + +// Constants for values that can be used in many of the tests as sample input or output values. +// +// Where a function returns `Optional`, `JsonField` or `JsonValue` There is no need to provide +// a value that matches the type ``, a simple `String` value of `"a-string"` will work OK. +internal const val STRING = "a-string" +internal val NULLABLE_STRING: String? = null +internal val OPTIONAL = Optional.of(STRING) +internal val JSON_FIELD = JsonField.of(STRING) +internal val JSON_VALUE = JsonValue.from(STRING) +internal val NULLABLE = null +internal const val BOOLEAN: Boolean = true +internal val NULLABLE_BOOLEAN: Boolean? = null +internal const val LONG: Long = 42L +internal val NULLABLE_LONG: Long? = null +internal const val DOUBLE: Double = 42.0 +internal val NULLABLE_DOUBLE: Double? = null +internal val LIST = listOf(STRING) +internal val SET = setOf(STRING) +internal val MAP = mapOf(STRING to STRING) + +/** + * Defines a test case where a function in a delegator returns a value from a corresponding function + * in a delegate. + */ +internal data class DelegationReadTestCase(val functionName: String, val expectedValue: Any) + +/** + * Defines a test case where a function in a delegator passes its parameters through to a + * corresponding function in a delegate. + */ +// Want `vararg`, so cannot use `data class`. Needs a custom `toString`, anyway. +internal class DelegationWriteTestCase( + val functionName: String, + /** + * The values to pass to the function being tested. If the first input value is `null`, it must + * be the only value. Only the first input value may be `null`, all others must be non-`null`. + * This is not enforced by this class, but is assumed by the related utility functions. + */ + vararg val inputValues: Any?, +) { + /** Gets the string representation that identifies the test function when running JUnit. */ + override fun toString(): String = + "$functionName(${inputValues.joinToString(", ") { + it?.javaClass?.simpleName ?: "null" + }})" +} + +/** A basic class used as the generic type when testing. */ +internal class X(val s: String) { + override fun equals(other: Any?) = other is X && other.s == s + + override fun hashCode() = s.hashCode() +} + +/** + * Checks that all functions in one class have a corresponding function with the same name and + * parameter types in another class. A list of function names that should be allowed as exceptions + * can be given. Non-public functions are ignored, as they are considered to be implementation + * details of each class. + * + * Call this function twice, changing the order of the two classes to ensure that both classes + * contain the same set of functions (barring exceptions), should that be the expectation. + * + * @param subsetClass The class whose functions should be a subset of the functions of the other + * class. + * @param supersetClass The class whose functions should be a superset of the functions of the other + * class. + */ +internal fun checkAllDelegation( + subsetClass: KClass<*>, + supersetClass: KClass<*>, + vararg exceptFunctionNames: String, +) { + assertThat(subsetClass != supersetClass) + .describedAs { "The two classes should not be the same." } + .isTrue + + val subsetFunctions = subsetClass.declaredFunctions + val missingFunctions = mutableListOf>() + + for (subsetFunction in subsetFunctions) { + if (subsetFunction.visibility != KVisibility.PUBLIC) { + continue + } + + if (subsetFunction.name in exceptFunctionNames) { + continue + } + + // Drop the first parameter from each function, as it is the implicit "this" object and has + // the type of the class declaring the function, which will never match. + val supersetFunction = + supersetClass.declaredFunctions.find { + it.name == subsetFunction.name && + it.parameters.drop(1).map { it.type } == + subsetFunction.parameters.drop(1).map { it.type } + } + + if (supersetFunction == null) { + missingFunctions.add(subsetFunction) + } + } + + assertThat(missingFunctions) + .describedAs { + "Function(s) not found in ${supersetClass.simpleName}:\n" + + missingFunctions.joinToString("\n") { " - $it" } + } + .isEmpty() +} + +/** + * Checks that the delegator function calls the corresponding delegate function and no other + * functions on the delegate. The test case defines the function name and the sample return value. + * All functions take no arguments. + */ +internal fun checkOneDelegationRead( + delegator: Any, + mockDelegate: Any, + testCase: DelegationReadTestCase, +) { + // Stub the method in the mock delegate using reflection + val delegateMethod = mockDelegate::class.java.getMethod(testCase.functionName) + `when`(delegateMethod.invoke(mockDelegate)).thenReturn(testCase.expectedValue) + + // Call the corresponding method on the delegator using reflection + val delegatorMethod = delegator::class.java.getMethod(testCase.functionName) + val result = delegatorMethod.invoke(delegator) + + // Verify that the corresponding method on the mock delegate was called exactly once + verify(mockDelegate, times(1)).apply { delegateMethod.invoke(mockDelegate) } + verifyNoMoreInteractions(mockDelegate) + + // Assert that the result matches the expected value + assertThat(result).isEqualTo(testCase.expectedValue) +} + +/** + * Checks that the delegator function calls the corresponding delegate function and no other + * functions on the delegate. The test case defines the function name and sample parameter values. + */ +internal fun checkOneDelegationWrite( + delegator: Any, + mockDelegate: Any, + testCase: DelegationWriteTestCase, +) { + invokeMethod(findDelegationMethod(delegator, testCase), delegator, testCase) + + // Verify that the corresponding method on the mock delegate was called exactly once. + verify(mockDelegate, times(1)).apply { + invokeMethod(findDelegationMethod(mockDelegate, testCase), mockDelegate, testCase) + } + verifyNoMoreInteractions(mockDelegate) +} + +private fun invokeMethod(method: Method, target: Any, testCase: DelegationWriteTestCase) { + val numParams = testCase.inputValues.size + val inputValue1 = testCase.inputValues[0] + val inputValue2 = testCase.inputValues.getOrNull(1) + + when (numParams) { + 1 -> method.invoke(target, inputValue1) + 2 -> method.invoke(target, inputValue1, inputValue2) + else -> fail { "Unexpected number of function parameters ($numParams)." } + } +} + +/** + * Finds the java method matching the test case's function name and parameter types in the delegator + * or delegate `target`. + */ +internal fun findDelegationMethod(target: Any, testCase: DelegationWriteTestCase): Method { + val numParams = testCase.inputValues.size + val inputValue1: Any? = testCase.inputValues[0] + val inputValue2 = if (numParams > 1) testCase.inputValues[1] else null + + val method = + when (numParams) { + 1 -> + if (inputValue1 != null) { + findJavaMethod( + target.javaClass, + testCase.functionName, + toJavaType(inputValue1.javaClass), + ) + } else { + // Only the first parameter may be nullable and only if it is the only + // parameter. If the first parameter is nullable, it will be the only function + // of the same name with a nullable first parameter. To handle the potentially + // nullable first parameter, Kotlin reflection is needed. This allows a function + // `f(Boolean)` to be distinguished from `f(Boolean?)`. For the tests, if the + // parameter type is nullable, the parameter value will always be `null` (if + // not, the function with the nullable parameter would not be matched). + // + // Using Kotlin reflection, the first parameter (zero index) is `this` object, + // so start matching from the second parameter onwards. + target::class + .declaredFunctions + .find { + it.name == testCase.functionName && + it.parameters[1].type.isMarkedNullable + } + ?.javaMethod + } + + 2 -> + if (inputValue1 != null && inputValue2 != null) { + findJavaMethod( + target.javaClass, + testCase.functionName, + toJavaType(inputValue1.javaClass), + toJavaType(inputValue2.javaClass), + ) + } else { + // There are no instances where there are two parameters and one of them is + // nullable. + fail { "Function $testCase second parameter must not be null." } + } + + else -> fail { "Function $testCase has unsupported number of parameters." } + } ?: fail { "Function $testCase cannot be found in $target." } + + // Using `fail` conditionally above, so the compiler knows the code will not continue and can + // infer that `method` is not null. It cannot do that for `assertThat...isNotNull`. + return method +} + +/** Finds a Java method in a class that matches a method name and a list of parameter types. */ +private fun findJavaMethod( + clazz: Class<*>, + methodName: String, + vararg parameterTypes: Class<*>, +): Method? = + clazz.declaredMethods.firstOrNull { method -> + method.name == methodName && + method.parameterTypes.size == parameterTypes.size && + method.parameterTypes.indices.all { index -> + (parameterTypes[index].isPrimitive && + method.parameterTypes[index] == parameterTypes[index]) || + method.parameterTypes[index].isAssignableFrom(parameterTypes[index]) + } + } + +/** + * Returns the Java type to use when matching type parameters for a Java method. The type is the + * type of the input value that will be used when the method is invoked. For most types, the given + * type is returned. However, if the type represents a Kotlin primitive, it will be converted to a + * Java primitive. This allows matching of methods with parameter types that are non-nullable Kotlin + * primitives. If not translated, methods with parameter types that are nullable Kotlin primitives + * would always be matched instead. + */ +private fun toJavaType(type: Class<*>) = + when (type) { + // This only needs to cover the types used in the test cases. + java.lang.Long::class.java -> java.lang.Long.TYPE + java.lang.Boolean::class.java -> java.lang.Boolean.TYPE + java.lang.Double::class.java -> java.lang.Double.TYPE + else -> type + } + +/** + * Checks that all delegating functions in a delegator class have corresponding unit tests. The + * read-only functions should take no parameters; only return a value. + * + * @param delegatorClass The delegator class whose functions are tested. Every named function in + * this class must be identified in one of the given sources of function names or a failure will + * occur. + * @param delegationTestCases The tests cases that identify the names of delegating functions for + * which parameterized unit tests have been defined. + * @param exceptionalTestedFns The names of delegating functions that are tested separately, not as + * parameterized unit tests. This is usually because they require special handling in the test. + * @param nonDelegatingFns The names of functions that do not perform any delegation and for which + * delegation tests are not required. + */ +internal fun checkAllDelegatorReadFunctionsAreTested( + delegatorClass: KClass<*>, + delegationTestCases: List, + exceptionalTestedFns: Set, + nonDelegatingFns: Set, +) { + val testedFns = delegationTestCases.map { it.functionName }.toSet() + exceptionalTestedFns + val delegatorFunctions = delegatorClass.declaredFunctions + val untestedFunctions = + delegatorFunctions.filter { it.name !in testedFns && it.name !in nonDelegatingFns } + + assertThat(untestedFunctions) + .describedAs( + "Delegation is not tested for function(s):\n" + + untestedFunctions.joinToString("\n") { " - $it" } + ) + .isEmpty() +} + +/** + * Checks that all delegating functions in a delegator class have corresponding unit tests. The + * write-only functions should take parameters and return no value. + * + * @param delegatorClass The delegator class whose functions are tested. Every named function in + * this class must be identified in one of the given sources of function names or a failure will + * occur. + * @param delegationTestCases The tests cases that identify the names of delegating functions for + * which parameterized unit tests have been defined. + * @param exceptionalTestedFns The names of delegating functions that are tested separately, not as + * parameterized unit tests. This is usually because they require special handling in the test. + * @param nonDelegatingFns The names of functions that do not perform any delegation and for which + * delegation tests are not required. + */ +internal fun checkAllDelegatorWriteFunctionsAreTested( + delegatorClass: KClass<*>, + delegationTestCases: List, + exceptionalTestedFns: Set, + nonDelegatingFns: Set, +) { + // There are exceptional test cases for some functions. Most other functions are part of the + // list of those using the parameterized test. There are many overloaded functions, so the + // approach here is to build a list (_not_ a set) of all function names and then "subtract" + // those for which tests are defined and see what remains. For example, there could be eight + // `addMessage` functions, so there must be eight tests defined for functions named `addMessage` + // that will be subtracted from the list of functions matching that name. Parameter types are + // not checked, as that is awkward and probably overkill. Therefore, this scheme is not reliable + // if a function is tested more than once. + val testedFns = + (delegationTestCases.map { it.functionName } + exceptionalTestedFns).toMutableList() + // Only interested in the names of the functions (which may contain duplicates): parameters are + // not matched, so any signatures could be misleading when reporting errors. + val delegatorFns = delegatorClass.declaredFunctions.map { it.name }.toMutableList() + + // Making modifications to the list, so clone it with `toList()` before iterating. + for (fnName in delegatorFns.toList()) { + if (fnName in testedFns) { + testedFns.remove(fnName) + delegatorFns.remove(fnName) + } + if (fnName in nonDelegatingFns) { + delegatorFns.remove(fnName) + } + } + + // If there are function names remaining in `delegatorFns`, then there are tests missing. + assertThat(delegatorFns) + .describedAs { "Delegation is not tested for functions $delegatorFns." } + .isEmpty() + + // If there are function names remaining in `testedFns`, then there are more tests than there + // should be. Functions might be tested twice, or there may be tests for functions that have + // since been removed from the delegate (though those tests probably failed). + assertThat(testedFns) + .describedAs { "Unexpected or redundant tests for functions $testedFns." } + .isEmpty() +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParamsTest.kt index 585aeb47..c81b13c3 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParamsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateParamsTest.kt @@ -16,6 +16,7 @@ internal class TranscriptionCreateParamsTest { TranscriptionCreateParams.builder() .file("some content".byteInputStream()) .model(AudioModel.WHISPER_1) + .chunkingStrategyAuto() .addInclude(TranscriptionInclude.LOGPROBS) .language("language") .prompt("prompt") @@ -31,6 +32,7 @@ internal class TranscriptionCreateParamsTest { TranscriptionCreateParams.builder() .file("some content".byteInputStream()) .model(AudioModel.WHISPER_1) + .chunkingStrategyAuto() .addInclude(TranscriptionInclude.LOGPROBS) .language("language") .prompt("prompt") @@ -53,6 +55,8 @@ internal class TranscriptionCreateParamsTest { mapOf( "file" to MultipartField.of("some content".byteInputStream()), "model" to MultipartField.of(AudioModel.WHISPER_1), + "chunking_strategy" to + MultipartField.of(TranscriptionCreateParams.ChunkingStrategy.ofAuto()), "include" to MultipartField.of(listOf(TranscriptionInclude.LOGPROBS)), "language" to MultipartField.of("language"), "prompt" to MultipartField.of("prompt"), diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateResponseTest.kt index 0a7c3f77..8f64d432 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateResponseTest.kt @@ -66,18 +66,18 @@ internal class TranscriptionCreateResponseTest { .addSegment( TranscriptionSegment.builder() .id(0L) - .avgLogprob(0.0) - .compressionRatio(0.0) - .end(0.0) - .noSpeechProb(0.0) + .avgLogprob(0.0f) + .compressionRatio(0.0f) + .end(0.0f) + .noSpeechProb(0.0f) .seek(0L) - .start(0.0) - .temperature(0.0) + .start(0.0f) + .temperature(0.0f) .text("text") .addToken(0L) .build() ) - .addWord(TranscriptionWord.builder().end(0.0).start(0.0).word("word").build()) + .addWord(TranscriptionWord.builder().end(0.0f).start(0.0f).word("word").build()) .build() val transcriptionCreateResponse = TranscriptionCreateResponse.ofVerbose(verbose) @@ -98,18 +98,18 @@ internal class TranscriptionCreateResponseTest { .addSegment( TranscriptionSegment.builder() .id(0L) - .avgLogprob(0.0) - .compressionRatio(0.0) - .end(0.0) - .noSpeechProb(0.0) + .avgLogprob(0.0f) + .compressionRatio(0.0f) + .end(0.0f) + .noSpeechProb(0.0f) .seek(0L) - .start(0.0) - .temperature(0.0) + .start(0.0f) + .temperature(0.0f) .text("text") .addToken(0L) .build() ) - .addWord(TranscriptionWord.builder().end(0.0).start(0.0).word("word").build()) + .addWord(TranscriptionWord.builder().end(0.0f).start(0.0f).word("word").build()) .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegmentTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegmentTest.kt index 127ee6b9..85eef238 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegmentTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionSegmentTest.kt @@ -14,25 +14,25 @@ internal class TranscriptionSegmentTest { val transcriptionSegment = TranscriptionSegment.builder() .id(0L) - .avgLogprob(0.0) - .compressionRatio(0.0) - .end(0.0) - .noSpeechProb(0.0) + .avgLogprob(0.0f) + .compressionRatio(0.0f) + .end(0.0f) + .noSpeechProb(0.0f) .seek(0L) - .start(0.0) - .temperature(0.0) + .start(0.0f) + .temperature(0.0f) .text("text") .addToken(0L) .build() 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.avgLogprob()).isEqualTo(0.0f) + assertThat(transcriptionSegment.compressionRatio()).isEqualTo(0.0f) + assertThat(transcriptionSegment.end()).isEqualTo(0.0f) + assertThat(transcriptionSegment.noSpeechProb()).isEqualTo(0.0f) assertThat(transcriptionSegment.seek()).isEqualTo(0L) - assertThat(transcriptionSegment.start()).isEqualTo(0.0) - assertThat(transcriptionSegment.temperature()).isEqualTo(0.0) + assertThat(transcriptionSegment.start()).isEqualTo(0.0f) + assertThat(transcriptionSegment.temperature()).isEqualTo(0.0f) assertThat(transcriptionSegment.text()).isEqualTo("text") assertThat(transcriptionSegment.tokens()).containsExactly(0L) } @@ -43,13 +43,13 @@ internal class TranscriptionSegmentTest { val transcriptionSegment = TranscriptionSegment.builder() .id(0L) - .avgLogprob(0.0) - .compressionRatio(0.0) - .end(0.0) - .noSpeechProb(0.0) + .avgLogprob(0.0f) + .compressionRatio(0.0f) + .end(0.0f) + .noSpeechProb(0.0f) .seek(0L) - .start(0.0) - .temperature(0.0) + .start(0.0f) + .temperature(0.0f) .text("text") .addToken(0L) .build() diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionVerboseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionVerboseTest.kt index 6c3ceef9..8f0a543f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionVerboseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionVerboseTest.kt @@ -20,18 +20,18 @@ internal class TranscriptionVerboseTest { .addSegment( TranscriptionSegment.builder() .id(0L) - .avgLogprob(0.0) - .compressionRatio(0.0) - .end(0.0) - .noSpeechProb(0.0) + .avgLogprob(0.0f) + .compressionRatio(0.0f) + .end(0.0f) + .noSpeechProb(0.0f) .seek(0L) - .start(0.0) - .temperature(0.0) + .start(0.0f) + .temperature(0.0f) .text("text") .addToken(0L) .build() ) - .addWord(TranscriptionWord.builder().end(0.0).start(0.0).word("word").build()) + .addWord(TranscriptionWord.builder().end(0.0f).start(0.0f).word("word").build()) .build() assertThat(transcriptionVerbose.duration()).isEqualTo(0.0) @@ -41,19 +41,19 @@ internal class TranscriptionVerboseTest { .containsExactly( TranscriptionSegment.builder() .id(0L) - .avgLogprob(0.0) - .compressionRatio(0.0) - .end(0.0) - .noSpeechProb(0.0) + .avgLogprob(0.0f) + .compressionRatio(0.0f) + .end(0.0f) + .noSpeechProb(0.0f) .seek(0L) - .start(0.0) - .temperature(0.0) + .start(0.0f) + .temperature(0.0f) .text("text") .addToken(0L) .build() ) assertThat(transcriptionVerbose.words().getOrNull()) - .containsExactly(TranscriptionWord.builder().end(0.0).start(0.0).word("word").build()) + .containsExactly(TranscriptionWord.builder().end(0.0f).start(0.0f).word("word").build()) } @Test @@ -67,18 +67,18 @@ internal class TranscriptionVerboseTest { .addSegment( TranscriptionSegment.builder() .id(0L) - .avgLogprob(0.0) - .compressionRatio(0.0) - .end(0.0) - .noSpeechProb(0.0) + .avgLogprob(0.0f) + .compressionRatio(0.0f) + .end(0.0f) + .noSpeechProb(0.0f) .seek(0L) - .start(0.0) - .temperature(0.0) + .start(0.0f) + .temperature(0.0f) .text("text") .addToken(0L) .build() ) - .addWord(TranscriptionWord.builder().end(0.0).start(0.0).word("word").build()) + .addWord(TranscriptionWord.builder().end(0.0f).start(0.0f).word("word").build()) .build() val roundtrippedTranscriptionVerbose = diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionWordTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionWordTest.kt index 0bf1311a..518a754e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionWordTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionWordTest.kt @@ -11,17 +11,19 @@ internal class TranscriptionWordTest { @Test fun create() { - val transcriptionWord = TranscriptionWord.builder().end(0.0).start(0.0).word("word").build() + val transcriptionWord = + TranscriptionWord.builder().end(0.0f).start(0.0f).word("word").build() - assertThat(transcriptionWord.end()).isEqualTo(0.0) - assertThat(transcriptionWord.start()).isEqualTo(0.0) + assertThat(transcriptionWord.end()).isEqualTo(0.0f) + assertThat(transcriptionWord.start()).isEqualTo(0.0f) assertThat(transcriptionWord.word()).isEqualTo("word") } @Test fun roundtrip() { val jsonMapper = jsonMapper() - val transcriptionWord = TranscriptionWord.builder().end(0.0).start(0.0).word("word").build() + val transcriptionWord = + TranscriptionWord.builder().end(0.0f).start(0.0f).word("word").build() val roundtrippedTranscriptionWord = jsonMapper.readValue( diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationCreateResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationCreateResponseTest.kt index 53fc2b4f..098a9b84 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationCreateResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationCreateResponseTest.kt @@ -50,13 +50,13 @@ internal class TranslationCreateResponseTest { .addSegment( TranscriptionSegment.builder() .id(0L) - .avgLogprob(0.0) - .compressionRatio(0.0) - .end(0.0) - .noSpeechProb(0.0) + .avgLogprob(0.0f) + .compressionRatio(0.0f) + .end(0.0f) + .noSpeechProb(0.0f) .seek(0L) - .start(0.0) - .temperature(0.0) + .start(0.0f) + .temperature(0.0f) .text("text") .addToken(0L) .build() @@ -81,13 +81,13 @@ internal class TranslationCreateResponseTest { .addSegment( TranscriptionSegment.builder() .id(0L) - .avgLogprob(0.0) - .compressionRatio(0.0) - .end(0.0) - .noSpeechProb(0.0) + .avgLogprob(0.0f) + .compressionRatio(0.0f) + .end(0.0f) + .noSpeechProb(0.0f) .seek(0L) - .start(0.0) - .temperature(0.0) + .start(0.0f) + .temperature(0.0f) .text("text") .addToken(0L) .build() diff --git a/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationVerboseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationVerboseTest.kt index fc5baf5d..7b318663 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationVerboseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/audio/translations/TranslationVerboseTest.kt @@ -21,13 +21,13 @@ internal class TranslationVerboseTest { .addSegment( TranscriptionSegment.builder() .id(0L) - .avgLogprob(0.0) - .compressionRatio(0.0) - .end(0.0) - .noSpeechProb(0.0) + .avgLogprob(0.0f) + .compressionRatio(0.0f) + .end(0.0f) + .noSpeechProb(0.0f) .seek(0L) - .start(0.0) - .temperature(0.0) + .start(0.0f) + .temperature(0.0f) .text("text") .addToken(0L) .build() @@ -41,13 +41,13 @@ internal class TranslationVerboseTest { .containsExactly( TranscriptionSegment.builder() .id(0L) - .avgLogprob(0.0) - .compressionRatio(0.0) - .end(0.0) - .noSpeechProb(0.0) + .avgLogprob(0.0f) + .compressionRatio(0.0f) + .end(0.0f) + .noSpeechProb(0.0f) .seek(0L) - .start(0.0) - .temperature(0.0) + .start(0.0f) + .temperature(0.0f) .text("text") .addToken(0L) .build() @@ -65,13 +65,13 @@ internal class TranslationVerboseTest { .addSegment( TranscriptionSegment.builder() .id(0L) - .avgLogprob(0.0) - .compressionRatio(0.0) - .end(0.0) - .noSpeechProb(0.0) + .avgLogprob(0.0f) + .compressionRatio(0.0f) + .end(0.0f) + .noSpeechProb(0.0f) .seek(0L) - .start(0.0) - .temperature(0.0) + .start(0.0f) + .temperature(0.0f) .text("text") .addToken(0L) .build() diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParamsTest.kt index e47aebd6..6ae11ba1 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParamsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParamsTest.kt @@ -347,4 +347,36 @@ internal class ChatCompletionCreateParamsTest { ) assertThat(body.model()).isEqualTo(ChatModel.GPT_4_1) } + + @Test + fun structuredOutputsBuilder() { + class X(val s: String) + + // Only interested in a few things: + // - Does the `Builder` type change when `responseFormat(Class)` is called? + // - Are values already set on the "old" `Builder` preserved in the change-over? + // - Can new values be set on the "new" `Builder` alongside the "old" values? + val params = + ChatCompletionCreateParams.builder() + .addDeveloperMessage("dev message") + .model(ChatModel.GPT_4_1) + .responseFormat(X::class.java) // Creates and return a new builder. + .addSystemMessage("sys message") + .build() + + val body = params.rawParams._body() + + assertThat(params).isInstanceOf(StructuredChatCompletionCreateParams::class.java) + assertThat(params.responseType).isEqualTo(X::class.java) + assertThat(body.messages()) + .containsExactly( + ChatCompletionMessageParam.ofDeveloper( + ChatCompletionDeveloperMessageParam.builder().content("dev message").build() + ), + ChatCompletionMessageParam.ofSystem( + ChatCompletionSystemMessageParam.builder().content("sys message").build() + ), + ) + assertThat(body.model()).isEqualTo(ChatModel.GPT_4_1) + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/StructuredChatCompletionCreateParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/StructuredChatCompletionCreateParamsTest.kt new file mode 100644 index 00000000..4b289198 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/StructuredChatCompletionCreateParamsTest.kt @@ -0,0 +1,318 @@ +package com.openai.models.chat.completions + +import com.openai.core.BOOLEAN +import com.openai.core.DOUBLE +import com.openai.core.DelegationWriteTestCase +import com.openai.core.JSON_FIELD +import com.openai.core.JSON_VALUE +import com.openai.core.LIST +import com.openai.core.LONG +import com.openai.core.MAP +import com.openai.core.NULLABLE +import com.openai.core.NULLABLE_BOOLEAN +import com.openai.core.NULLABLE_DOUBLE +import com.openai.core.NULLABLE_LONG +import com.openai.core.OPTIONAL +import com.openai.core.SET +import com.openai.core.STRING +import com.openai.core.X +import com.openai.core.checkAllDelegation +import com.openai.core.checkAllDelegatorWriteFunctionsAreTested +import com.openai.core.checkOneDelegationWrite +import com.openai.core.findDelegationMethod +import com.openai.core.http.Headers +import com.openai.core.http.QueryParams +import com.openai.core.responseFormatFromClass +import com.openai.models.ChatModel +import com.openai.models.FunctionDefinition +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import org.mockito.Mockito.mock +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.kotlin.times +import org.mockito.kotlin.verify + +/** + * Unit tests for the [StructuredChatCompletionCreateParams] class (delegator) and its delegation of + * most functions to a wrapped [ChatCompletionCreateParams] (delegate). It is the `Builder` class of + * each main class that is involved in the delegation. The tests include confirmation of the + * following: + * - All functions in the delegator correspond to a function in the delegate and _vice versa_. + * - All functions in the delegator call their corresponding function in the delegate and only that + * function. + * - A unit test exists for all functions. + * + * There are some exceptions to the above that are handled differently. + */ +internal class StructuredChatCompletionCreateParamsTest { + companion object { + private val CHAT_MODEL = ChatModel.GPT_4 + + private val MESSAGE = + ChatCompletionMessage.builder().content(STRING).refusal(STRING).build() + + private val USER_MESSAGE_PARAM = + ChatCompletionUserMessageParam.builder().content(STRING).build() + private val DEV_MESSAGE_PARAM = + ChatCompletionDeveloperMessageParam.builder().content(STRING).build() + private val SYS_MESSAGE_PARAM = + ChatCompletionSystemMessageParam.builder().content(STRING).build() + private val ASSIST_MESSAGE_PARAM = + ChatCompletionAssistantMessageParam.builder().content(STRING).build() + private val TOOL_MESSAGE_PARAM = + ChatCompletionToolMessageParam.builder().content(STRING).toolCallId(STRING).build() + private val FUNC_MESSAGE_PARAM = + ChatCompletionFunctionMessageParam.builder().content(STRING).name(STRING).build() + private val MESSAGE_PARAM = ChatCompletionMessageParam.ofUser(USER_MESSAGE_PARAM) + + private val DEV_MESSAGE_PARAM_CONTENT = + ChatCompletionDeveloperMessageParam.Content.ofText(STRING) + private val SYS_MESSAGE_PARAM_CONTENT = + ChatCompletionSystemMessageParam.Content.ofText(STRING) + private val USER_MESSAGE_PARAM_CONTENT = + ChatCompletionUserMessageParam.Content.ofText(STRING) + + private val PARAMS_BODY = + ChatCompletionCreateParams.Body.builder() + .messages(listOf(MESSAGE_PARAM)) + .model(CHAT_MODEL) + .build() + private val WEB_SEARCH_OPTIONS = + ChatCompletionCreateParams.WebSearchOptions.builder().build() + + private val FUNCTION_CALL_MODE = + ChatCompletionCreateParams.FunctionCall.FunctionCallMode.AUTO + private val FUNCTION_CALL_OPTION = + ChatCompletionFunctionCallOption.builder().name(STRING).build() + private val FUNCTION_CALL = + ChatCompletionCreateParams.FunctionCall.ofFunctionCallOption(FUNCTION_CALL_OPTION) + + private val FUNCTION = ChatCompletionCreateParams.Function.builder().name(STRING).build() + private val METADATA = ChatCompletionCreateParams.Metadata.builder().build() + private val MODALITY = ChatCompletionCreateParams.Modality.TEXT + private val FUNCTION_DEFINITION = FunctionDefinition.builder().name(STRING).build() + private val TOOL = ChatCompletionTool.builder().function(FUNCTION_DEFINITION).build() + + private val NAMED_TOOL_CHOICE_FUNCTION = + ChatCompletionNamedToolChoice.Function.builder().name(STRING).build() + private val NAMED_TOOL_CHOICE = + ChatCompletionNamedToolChoice.builder().function(NAMED_TOOL_CHOICE_FUNCTION).build() + private val TOOL_CHOICE_OPTION_AUTO = ChatCompletionToolChoiceOption.Auto.AUTO + private val TOOL_CHOICE_OPTION = + ChatCompletionToolChoiceOption.ofAuto(TOOL_CHOICE_OPTION_AUTO) + + private val HEADERS = Headers.builder().build() + private val QUERY_PARAMS = QueryParams.builder().build() + + // The list order follows the declaration order in `ChatCompletionCreateParams.Builder` for + // easier maintenance. + @JvmStatic + private fun builderDelegationTestCases() = + listOf( + DelegationWriteTestCase("body", PARAMS_BODY), + DelegationWriteTestCase("messages", LIST), + DelegationWriteTestCase("messages", JSON_FIELD), + DelegationWriteTestCase("addMessage", MESSAGE_PARAM), + DelegationWriteTestCase("addMessage", DEV_MESSAGE_PARAM), + DelegationWriteTestCase("addDeveloperMessage", DEV_MESSAGE_PARAM_CONTENT), + DelegationWriteTestCase("addDeveloperMessage", STRING), + DelegationWriteTestCase("addDeveloperMessageOfArrayOfContentParts", LIST), + DelegationWriteTestCase("addMessage", SYS_MESSAGE_PARAM), + DelegationWriteTestCase("addSystemMessage", SYS_MESSAGE_PARAM_CONTENT), + DelegationWriteTestCase("addSystemMessage", STRING), + DelegationWriteTestCase("addSystemMessageOfArrayOfContentParts", LIST), + DelegationWriteTestCase("addMessage", USER_MESSAGE_PARAM), + DelegationWriteTestCase("addUserMessage", USER_MESSAGE_PARAM_CONTENT), + DelegationWriteTestCase("addUserMessage", STRING), + DelegationWriteTestCase("addUserMessageOfArrayOfContentParts", LIST), + DelegationWriteTestCase("addMessage", ASSIST_MESSAGE_PARAM), + DelegationWriteTestCase("addMessage", MESSAGE), + DelegationWriteTestCase("addMessage", TOOL_MESSAGE_PARAM), + DelegationWriteTestCase("addMessage", FUNC_MESSAGE_PARAM), + DelegationWriteTestCase("model", CHAT_MODEL), + DelegationWriteTestCase("model", JSON_FIELD), + DelegationWriteTestCase("model", STRING), + DelegationWriteTestCase("audio", NULLABLE), + DelegationWriteTestCase("audio", OPTIONAL), + DelegationWriteTestCase("audio", JSON_FIELD), + DelegationWriteTestCase("frequencyPenalty", NULLABLE_DOUBLE), + DelegationWriteTestCase("frequencyPenalty", DOUBLE), + DelegationWriteTestCase("frequencyPenalty", OPTIONAL), + DelegationWriteTestCase("frequencyPenalty", JSON_FIELD), + DelegationWriteTestCase("functionCall", FUNCTION_CALL), + DelegationWriteTestCase("functionCall", JSON_FIELD), + DelegationWriteTestCase("functionCall", FUNCTION_CALL_MODE), + DelegationWriteTestCase("functionCall", FUNCTION_CALL_OPTION), + DelegationWriteTestCase("functions", LIST), + DelegationWriteTestCase("functions", JSON_FIELD), + DelegationWriteTestCase("addFunction", FUNCTION), + DelegationWriteTestCase("logitBias", NULLABLE), + DelegationWriteTestCase("logitBias", OPTIONAL), + DelegationWriteTestCase("logitBias", JSON_FIELD), + DelegationWriteTestCase("logprobs", NULLABLE_BOOLEAN), + DelegationWriteTestCase("logprobs", BOOLEAN), + DelegationWriteTestCase("logprobs", OPTIONAL), + DelegationWriteTestCase("logprobs", JSON_FIELD), + DelegationWriteTestCase("maxCompletionTokens", NULLABLE_LONG), + DelegationWriteTestCase("maxCompletionTokens", LONG), + DelegationWriteTestCase("maxCompletionTokens", OPTIONAL), + DelegationWriteTestCase("maxCompletionTokens", JSON_FIELD), + DelegationWriteTestCase("maxTokens", NULLABLE_LONG), + DelegationWriteTestCase("maxTokens", LONG), + DelegationWriteTestCase("maxTokens", OPTIONAL), + DelegationWriteTestCase("maxTokens", JSON_FIELD), + DelegationWriteTestCase("metadata", METADATA), + DelegationWriteTestCase("metadata", OPTIONAL), + DelegationWriteTestCase("metadata", JSON_FIELD), + DelegationWriteTestCase("modalities", LIST), + DelegationWriteTestCase("modalities", OPTIONAL), + DelegationWriteTestCase("modalities", JSON_FIELD), + DelegationWriteTestCase("addModality", MODALITY), + DelegationWriteTestCase("n", NULLABLE_LONG), + DelegationWriteTestCase("n", LONG), + DelegationWriteTestCase("n", OPTIONAL), + DelegationWriteTestCase("n", JSON_FIELD), + DelegationWriteTestCase("parallelToolCalls", BOOLEAN), + DelegationWriteTestCase("parallelToolCalls", JSON_FIELD), + DelegationWriteTestCase("prediction", NULLABLE), + DelegationWriteTestCase("prediction", OPTIONAL), + DelegationWriteTestCase("prediction", JSON_FIELD), + DelegationWriteTestCase("presencePenalty", NULLABLE_DOUBLE), + DelegationWriteTestCase("presencePenalty", DOUBLE), + DelegationWriteTestCase("presencePenalty", OPTIONAL), + DelegationWriteTestCase("presencePenalty", JSON_FIELD), + DelegationWriteTestCase("reasoningEffort", NULLABLE), + DelegationWriteTestCase("reasoningEffort", OPTIONAL), + DelegationWriteTestCase("reasoningEffort", JSON_FIELD), + // `responseFormat()` is a special case and has its own unit test. + DelegationWriteTestCase("seed", NULLABLE_LONG), + DelegationWriteTestCase("seed", LONG), + DelegationWriteTestCase("seed", OPTIONAL), + DelegationWriteTestCase("seed", JSON_FIELD), + DelegationWriteTestCase("serviceTier", NULLABLE), + DelegationWriteTestCase("serviceTier", OPTIONAL), + DelegationWriteTestCase("serviceTier", JSON_FIELD), + DelegationWriteTestCase("stop", NULLABLE), + DelegationWriteTestCase("stop", OPTIONAL), + DelegationWriteTestCase("stop", JSON_FIELD), + DelegationWriteTestCase("stop", STRING), + DelegationWriteTestCase("stopOfStrings", LIST), + DelegationWriteTestCase("store", NULLABLE_BOOLEAN), + DelegationWriteTestCase("store", BOOLEAN), + DelegationWriteTestCase("store", OPTIONAL), + DelegationWriteTestCase("store", JSON_FIELD), + DelegationWriteTestCase("streamOptions", NULLABLE), + DelegationWriteTestCase("streamOptions", OPTIONAL), + DelegationWriteTestCase("streamOptions", JSON_FIELD), + DelegationWriteTestCase("temperature", NULLABLE_DOUBLE), + DelegationWriteTestCase("temperature", DOUBLE), + DelegationWriteTestCase("temperature", OPTIONAL), + DelegationWriteTestCase("temperature", JSON_FIELD), + DelegationWriteTestCase("toolChoice", TOOL_CHOICE_OPTION), + DelegationWriteTestCase("toolChoice", JSON_FIELD), + DelegationWriteTestCase("toolChoice", TOOL_CHOICE_OPTION_AUTO), + DelegationWriteTestCase("toolChoice", NAMED_TOOL_CHOICE), + DelegationWriteTestCase("tools", LIST), + DelegationWriteTestCase("tools", JSON_FIELD), + DelegationWriteTestCase("addTool", TOOL), + DelegationWriteTestCase("topLogprobs", NULLABLE_LONG), + DelegationWriteTestCase("topLogprobs", LONG), + DelegationWriteTestCase("topLogprobs", OPTIONAL), + DelegationWriteTestCase("topLogprobs", JSON_FIELD), + DelegationWriteTestCase("topP", NULLABLE_DOUBLE), + DelegationWriteTestCase("topP", DOUBLE), + DelegationWriteTestCase("topP", OPTIONAL), + DelegationWriteTestCase("topP", JSON_FIELD), + DelegationWriteTestCase("user", STRING), + DelegationWriteTestCase("user", JSON_FIELD), + DelegationWriteTestCase("webSearchOptions", WEB_SEARCH_OPTIONS), + DelegationWriteTestCase("webSearchOptions", JSON_FIELD), + DelegationWriteTestCase("additionalBodyProperties", MAP), + DelegationWriteTestCase("putAdditionalBodyProperty", STRING, JSON_VALUE), + DelegationWriteTestCase("putAllAdditionalBodyProperties", MAP), + DelegationWriteTestCase("removeAdditionalBodyProperty", STRING), + DelegationWriteTestCase("removeAllAdditionalBodyProperties", SET), + DelegationWriteTestCase("additionalHeaders", HEADERS), + DelegationWriteTestCase("additionalHeaders", MAP), + DelegationWriteTestCase("putAdditionalHeader", STRING, STRING), + DelegationWriteTestCase("putAdditionalHeaders", STRING, LIST), + DelegationWriteTestCase("putAllAdditionalHeaders", HEADERS), + DelegationWriteTestCase("putAllAdditionalHeaders", MAP), + DelegationWriteTestCase("replaceAdditionalHeaders", STRING, STRING), + DelegationWriteTestCase("replaceAdditionalHeaders", STRING, LIST), + DelegationWriteTestCase("replaceAllAdditionalHeaders", HEADERS), + DelegationWriteTestCase("replaceAllAdditionalHeaders", MAP), + DelegationWriteTestCase("removeAdditionalHeaders", STRING), + DelegationWriteTestCase("removeAllAdditionalHeaders", SET), + DelegationWriteTestCase("additionalQueryParams", QUERY_PARAMS), + DelegationWriteTestCase("additionalQueryParams", MAP), + DelegationWriteTestCase("putAdditionalQueryParam", STRING, STRING), + DelegationWriteTestCase("putAdditionalQueryParams", STRING, LIST), + DelegationWriteTestCase("putAllAdditionalQueryParams", QUERY_PARAMS), + DelegationWriteTestCase("putAllAdditionalQueryParams", MAP), + DelegationWriteTestCase("replaceAdditionalQueryParams", STRING, STRING), + DelegationWriteTestCase("replaceAdditionalQueryParams", STRING, LIST), + DelegationWriteTestCase("replaceAllAdditionalQueryParams", QUERY_PARAMS), + DelegationWriteTestCase("replaceAllAdditionalQueryParams", MAP), + DelegationWriteTestCase("removeAdditionalQueryParams", STRING), + DelegationWriteTestCase("removeAllAdditionalQueryParams", SET), + ) + } + + // New instances of the `mockBuilderDelegate` and `builderDelegator` are required for each test + // case (each test case runs in its own instance of the test class). + private val mockBuilderDelegate: ChatCompletionCreateParams.Builder = + mock(ChatCompletionCreateParams.Builder::class.java) + private val builderDelegator = + StructuredChatCompletionCreateParams.builder().inject(mockBuilderDelegate) + + @Test + fun allBuilderDelegateFunctionsExistInDelegator() { + // The delegator class does not implement the various `responseFormat` functions of the + // delegate class. + checkAllDelegation(mockBuilderDelegate::class, builderDelegator::class, "responseFormat") + } + + @Test + fun allBuilderDelegatorFunctionsExistInDelegate() { + // The delegator implements a different `responseFormat` function from those overloads in + // the delegate class. + checkAllDelegation(builderDelegator::class, mockBuilderDelegate::class, "responseFormat") + } + + @Test + fun allBuilderDelegatorFunctionsAreTested() { + checkAllDelegatorWriteFunctionsAreTested( + builderDelegator::class, + builderDelegationTestCases(), + exceptionalTestedFns = setOf("responseFormat"), + nonDelegatingFns = setOf("build", "wrap", "inject"), + ) + } + + @ParameterizedTest + @MethodSource("builderDelegationTestCases") + fun `delegation of Builder write functions`(testCase: DelegationWriteTestCase) { + checkOneDelegationWrite(builderDelegator, mockBuilderDelegate, testCase) + } + + @Test + fun `delegation of responseFormat`() { + // Special unit test case as the delegator method signature does not match that of the + // delegate method. + val delegatorTestCase = DelegationWriteTestCase("responseFormat", X::class.java) + val delegatorMethod = findDelegationMethod(builderDelegator, delegatorTestCase) + val mockDelegateTestCase = + DelegationWriteTestCase("responseFormat", responseFormatFromClass(X::class.java)) + val mockDelegateMethod = findDelegationMethod(mockBuilderDelegate, mockDelegateTestCase) + + delegatorMethod.invoke(builderDelegator, delegatorTestCase.inputValues[0]) + + // Verify that the corresponding method on the mock delegate was called exactly once. + verify(mockBuilderDelegate, times(1)).apply { + mockDelegateMethod.invoke(mockBuilderDelegate, mockDelegateTestCase.inputValues[0]) + } + verifyNoMoreInteractions(mockBuilderDelegate) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/StructuredChatCompletionMessageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/StructuredChatCompletionMessageTest.kt new file mode 100644 index 00000000..939f9a53 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/StructuredChatCompletionMessageTest.kt @@ -0,0 +1,129 @@ +package com.openai.models.chat.completions + +import com.openai.core.DelegationReadTestCase +import com.openai.core.JSON_FIELD +import com.openai.core.JSON_VALUE +import com.openai.core.JsonField +import com.openai.core.OPTIONAL +import com.openai.core.STRING +import com.openai.core.X +import com.openai.core.checkAllDelegation +import com.openai.core.checkAllDelegatorReadFunctionsAreTested +import com.openai.core.checkOneDelegationRead +import java.util.Optional +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import org.mockito.Mockito.mock +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.Mockito.`when` +import org.mockito.kotlin.times +import org.mockito.kotlin.verify + +/** + * Unit tests for the [StructuredChatCompletionMessage] class (delegator) and its delegation of most + * functions to a wrapped [ChatCompletionMessage] (delegate). The tests include confirmation of the + * following: + * - All functions in the delegator correspond to a function in the delegate and _vice versa_. + * - All functions in the delegator call their corresponding function in the delegate and only that + * function. + * - A unit test exists for all functions. + * + * There are some exceptions to the above that are handled differently. + */ +internal class StructuredChatCompletionMessageTest { + companion object { + private val MESSAGE = + ChatCompletionMessage.builder().content(STRING).refusal(STRING).build() + + // The list order follows the declaration order in `ChatCompletionMessage` for easier + // maintenance. + @JvmStatic + private fun delegationTestCases() = + listOf( + // `content()` is a special case and has its own test function. + DelegationReadTestCase("refusal", OPTIONAL), + DelegationReadTestCase("_role", JSON_VALUE), + DelegationReadTestCase("annotations", OPTIONAL), + DelegationReadTestCase("audio", OPTIONAL), + DelegationReadTestCase("functionCall", OPTIONAL), + DelegationReadTestCase("toolCalls", OPTIONAL), + // `_content()` is a special case and has its own test function. + DelegationReadTestCase("_refusal", JSON_FIELD), + DelegationReadTestCase("_annotations", JSON_FIELD), + DelegationReadTestCase("_audio", JSON_FIELD), + DelegationReadTestCase("_functionCall", JSON_FIELD), + DelegationReadTestCase("_toolCalls", JSON_FIELD), + DelegationReadTestCase("_additionalProperties", mapOf("key" to JSON_VALUE)), + DelegationReadTestCase("validate", MESSAGE), + // For this boolean function, call with both possible values to ensure that any + // hard-coding or default value will not result in a false positive test. + DelegationReadTestCase("isValid", true), + DelegationReadTestCase("isValid", false), + ) + } + + // New instances of the `mockDelegate` and `delegator` are required for each test case (each + // test case runs in its own instance of the test class). + private val mockDelegate: ChatCompletionMessage = mock(ChatCompletionMessage::class.java) + private val delegator = StructuredChatCompletionMessage(X::class.java, mockDelegate) + + @Test + fun allDelegateFunctionsExistInDelegator() { + checkAllDelegation(mockDelegate::class, delegator::class, "toBuilder", "toParam") + } + + @Test + fun allDelegatorFunctionsExistInDelegate() { + checkAllDelegation(delegator::class, mockDelegate::class) + } + + @Test + fun allDelegatorFunctionsAreTested() { + // There are exceptional test cases for some functions. Most other functions are part of the + // list of those using the parameterized test. A few delegator functions do not delegate, so + // no test function is necessary. + checkAllDelegatorReadFunctionsAreTested( + delegator::class, + delegationTestCases(), + exceptionalTestedFns = setOf("content", "_content"), + nonDelegatingFns = setOf("equals", "hashCode", "toString"), + ) + } + + @ParameterizedTest + @MethodSource("delegationTestCases") + fun `delegation of functions in general`(testCase: DelegationReadTestCase) { + checkOneDelegationRead(delegator, mockDelegate, testCase) + } + + @Test + fun `delegation of content`() { + // Input and output are different types, so this test is an exceptional case. + // `content()` (without an underscore) delegates to `_content()` (with an underscore) + // indirectly via the `content` field initializer. + val input = JsonField.of("{\"s\" : \"hello\"}") + `when`(mockDelegate._content()).thenReturn(input) + val output = delegator.content() // Without an underscore. + + verify(mockDelegate, times(1))._content() + verifyNoMoreInteractions(mockDelegate) + + assertThat(output).isEqualTo(Optional.of(X("hello"))) + } + + @Test + fun `delegation of _content`() { + // Input and output are different types, so this test is an exceptional case. + // `_content()` delegates to `_content()` indirectly via the `content` field initializer. + val input = JsonField.of("{\"s\" : \"hello\"}") + `when`(mockDelegate._content()).thenReturn(input) + val output = delegator._content() // With an underscore. + + verify(mockDelegate, times(1))._content() + verifyNoMoreInteractions(mockDelegate) + + assertThat(output).isEqualTo(JsonField.of(X("hello"))) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/StructuredChatCompletionTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/StructuredChatCompletionTest.kt new file mode 100644 index 00000000..40dc509f --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/StructuredChatCompletionTest.kt @@ -0,0 +1,307 @@ +package com.openai.models.chat.completions + +import com.openai.core.DelegationReadTestCase +import com.openai.core.JSON_FIELD +import com.openai.core.JSON_VALUE +import com.openai.core.JsonField +import com.openai.core.LONG +import com.openai.core.OPTIONAL +import com.openai.core.STRING +import com.openai.core.X +import com.openai.core.checkAllDelegation +import com.openai.core.checkAllDelegatorReadFunctionsAreTested +import com.openai.core.checkOneDelegationRead +import com.openai.errors.OpenAIInvalidDataException +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import org.mockito.Mockito.mock +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.Mockito.`when` +import org.mockito.kotlin.times +import org.mockito.kotlin.verify + +/** + * Unit tests for the [StructuredChatCompletion] class (delegator) and its delegation of most + * functions to a wrapped [ChatCompletion] (delegate). The tests include confirmation of the + * following: + * - All functions in the delegator correspond to a function in the delegate and _vice versa_. + * - All functions in the delegator call their corresponding function in the delegate and only that + * function. + * - A unit test exists for all functions. + * + * There are some exceptions to the above that are handled differently. + */ +internal class StructuredChatCompletionTest { + companion object { + private val MESSAGE = + ChatCompletionMessage.builder().content(STRING).refusal(STRING).build() + private val FINISH_REASON = ChatCompletion.Choice.FinishReason.STOP + private val CHOICE = + ChatCompletion.Choice.builder() + .message(MESSAGE) + .index(0L) + .finishReason(FINISH_REASON) + .logprobs( + ChatCompletion.Choice.Logprobs.builder().content(null).refusal(null).build() + ) + .build() + + // The list order follows the declaration order in `ChatCompletion` for easier maintenance. + @JvmStatic + private fun delegationTestCases() = + listOf( + DelegationReadTestCase("id", STRING), + // `choices()` is a special case and has its own test function. + DelegationReadTestCase("created", LONG), + DelegationReadTestCase("model", STRING), + DelegationReadTestCase("_object_", JSON_VALUE), + DelegationReadTestCase("serviceTier", OPTIONAL), + DelegationReadTestCase("systemFingerprint", OPTIONAL), + DelegationReadTestCase("usage", OPTIONAL), + DelegationReadTestCase("_id", JSON_FIELD), + // `_choices()` is a special case and has its own test function. + DelegationReadTestCase("_created", JSON_FIELD), + DelegationReadTestCase("_model", JSON_FIELD), + DelegationReadTestCase("_serviceTier", JSON_FIELD), + DelegationReadTestCase("_systemFingerprint", JSON_FIELD), + DelegationReadTestCase("_usage", JSON_FIELD), + DelegationReadTestCase("_additionalProperties", mapOf("key" to JSON_VALUE)), + // `validate()` and `isValid()` (which calls `validate()`) are tested separately, + // as they require special handling. + ) + + @JvmStatic + private fun choiceDelegationTestCases() = + listOf( + DelegationReadTestCase("finishReason", FINISH_REASON), + DelegationReadTestCase("index", LONG), + DelegationReadTestCase("logprobs", OPTIONAL), + DelegationReadTestCase("_finishReason", JSON_FIELD), + // `message()` is a special case and has its own test function. + DelegationReadTestCase("_index", JSON_FIELD), + DelegationReadTestCase("_logprobs", JSON_FIELD), + // `_message()` is a special case and has its own test function. + DelegationReadTestCase("_additionalProperties", mapOf("key" to JSON_VALUE)), + // `validate()` and `isValid()` (which calls `validate()`) are tested separately, + // as they require special handling. + ) + } + + // New instances of the `mockDelegate` and `delegator` are required for each test case (each + // test case runs in its own instance of the test class). + private val mockDelegate: ChatCompletion = mock(ChatCompletion::class.java) + private val delegator = StructuredChatCompletion(X::class.java, mockDelegate) + + private val mockChoiceDelegate: ChatCompletion.Choice = mock(ChatCompletion.Choice::class.java) + private val choiceDelegator = + StructuredChatCompletion.Choice(X::class.java, mockChoiceDelegate) + + @Test + fun allChatCompletionDelegateFunctionsExistInDelegator() { + checkAllDelegation(mockDelegate::class, delegator::class, "toBuilder") + } + + @Test + fun allChatCompletionDelegatorFunctionsExistInDelegate() { + checkAllDelegation(delegator::class, mockDelegate::class) + } + + @Test + fun allChoiceDelegateFunctionsExistInDelegator() { + checkAllDelegation(mockChoiceDelegate::class, choiceDelegator::class, "toBuilder") + } + + @Test + fun allChoiceDelegatorFunctionsExistInDelegate() { + checkAllDelegation(choiceDelegator::class, mockChoiceDelegate::class) + } + + @Test + fun allDelegatorFunctionsAreTested() { + // There are exceptional test cases for some functions. Most other functions are part of the + // list of those using the parameterized test. A few delegator functions do not delegate, so + // no test function is necessary. + checkAllDelegatorReadFunctionsAreTested( + delegator::class, + delegationTestCases(), + exceptionalTestedFns = setOf("choices", "_choices", "validate", "isValid"), + nonDelegatingFns = setOf("equals", "hashCode", "toString"), + ) + } + + @Test + fun allChoiceDelegatorFunctionsAreTested() { + checkAllDelegatorReadFunctionsAreTested( + choiceDelegator::class, + choiceDelegationTestCases(), + exceptionalTestedFns = setOf("message", "_message", "validate", "isValid"), + nonDelegatingFns = setOf("equals", "hashCode", "toString"), + ) + } + + @ParameterizedTest + @MethodSource("delegationTestCases") + fun `delegation of functions in general`(testCase: DelegationReadTestCase) { + checkOneDelegationRead(delegator, mockDelegate, testCase) + } + + @ParameterizedTest + @MethodSource("choiceDelegationTestCases") + fun `delegation of Choice functions in general`(testCase: DelegationReadTestCase) { + checkOneDelegationRead(choiceDelegator, mockChoiceDelegate, testCase) + } + + @Test + fun `delegation of choices`() { + // Input and output are different types, so this test is an exceptional case. + // `choices()` (without an underscore) delegates to `_choices()` (with an underscore) + // indirectly via the `choices` field initializer. + val input = JsonField.of(listOf(CHOICE)) + `when`(mockDelegate._choices()).thenReturn(input) + val output = delegator.choices() // Without an underscore. + + verify(mockDelegate, times(1))._choices() + verifyNoMoreInteractions(mockDelegate) + + assertThat(output[0].rawChoice).isEqualTo(CHOICE) + } + + @Test + fun `delegation of _choices`() { + // Input and output are different types, so this test is an exceptional case. + // `_choices()` delegates to `_choices()` indirectly via the `choices` field initializer. + val input = JsonField.of(listOf(CHOICE)) + `when`(mockDelegate._choices()).thenReturn(input) + val output = delegator._choices() // With an underscore. + + verify(mockDelegate, times(1))._choices() + verifyNoMoreInteractions(mockDelegate) + + assertThat(output.getRequired("_choices")[0].rawChoice).isEqualTo(CHOICE) + } + + @Test + fun `delegation of validate`() { + val input = JsonField.of(listOf(CHOICE)) + `when`(mockDelegate._choices()).thenReturn(input) + val output = delegator.validate() + + // `validate()` calls `choices()` on the delegator which triggers the lazy initializer which + // calls `_choices()` on the delegate before `validate()` also calls `validate()` on the + // delegate. + verify(mockDelegate, times(1))._choices() + verify(mockDelegate, times(1)).validate() + verifyNoMoreInteractions(mockDelegate) + + assertThat(output).isSameAs(delegator) + } + + @Test + fun `delegation of isValid when true`() { + val input = JsonField.of(listOf(CHOICE)) + `when`(mockDelegate._choices()).thenReturn(input) + val output = delegator.isValid() + + // `isValid()` calls `validate()`, which has side effects explained in its test function. + verify(mockDelegate, times(1))._choices() + verify(mockDelegate, times(1)).validate() + verifyNoMoreInteractions(mockDelegate) + + assertThat(output).isTrue + } + + @Test + fun `delegation of isValid when false`() { + // Try with a `false` value to make sure `isValid()` is not just hard-coded to `true`. Do + // this by making `validate()` on the delegate throw an exception. + val input = JsonField.of(listOf(CHOICE)) + `when`(mockDelegate._choices()).thenReturn(input) + `when`(mockDelegate.validate()).thenThrow(OpenAIInvalidDataException("test")) + val output = delegator.isValid() + + // `isValid()` calls `validate()`, which has side effects explained in its test function. + verify(mockDelegate, times(1))._choices() + verify(mockDelegate, times(1)).validate() + verifyNoMoreInteractions(mockDelegate) + + assertThat(output).isFalse + } + + @Test + fun `delegation of Choice-message`() { + // Input and output are different types, so this test is an exceptional case. + // `message()` (without an underscore) delegates to `_message()` (with an underscore) + // indirectly via the `message` field initializer. + val input = JsonField.of(MESSAGE) + `when`(mockChoiceDelegate._message()).thenReturn(input) + val output = choiceDelegator.message() // Without an underscore. + + verify(mockChoiceDelegate, times(1))._message() + verifyNoMoreInteractions(mockChoiceDelegate) + + assertThat(output.rawMessage).isEqualTo(MESSAGE) + } + + @Test + fun `delegation of Choice-_message`() { + // Input and output are different types, so this test is an exceptional case. + // `_message()` delegates to `_message()` indirectly via the `message` field initializer. + val input = JsonField.of(MESSAGE) + `when`(mockChoiceDelegate._message()).thenReturn(input) + val output = choiceDelegator._message() // With an underscore. + + verify(mockChoiceDelegate, times(1))._message() + verifyNoMoreInteractions(mockChoiceDelegate) + + assertThat(output.getRequired("_message").rawMessage).isEqualTo(MESSAGE) + } + + @Test + fun `delegation of Choice-validate`() { + val input = JsonField.of(MESSAGE) + `when`(mockChoiceDelegate._message()).thenReturn(input) + val output = choiceDelegator.validate() + + // `validate()` calls `message()` on the delegator which triggers the lazy initializer which + // calls `_message()` on the delegate before `validate()` also calls `validate()` on the + // delegate. + verify(mockChoiceDelegate, times(1))._message() + verify(mockChoiceDelegate, times(1)).validate() + verifyNoMoreInteractions(mockChoiceDelegate) + + assertThat(output).isSameAs(choiceDelegator) + } + + @Test + fun `delegation of Choice-isValid when true`() { + val input = JsonField.of(MESSAGE) + `when`(mockChoiceDelegate._message()).thenReturn(input) + val output = choiceDelegator.isValid() + + // `isValid()` calls `validate()`, which has side effects explained in its test function. + verify(mockChoiceDelegate, times(1))._message() + verify(mockChoiceDelegate, times(1)).validate() + verifyNoMoreInteractions(mockChoiceDelegate) + + assertThat(output).isTrue + } + + @Test + fun `delegation of Choice-isValid when false`() { + // Try with a `false` value to make sure `isValid()` is not just hard-coded to `true`. Do + // this by making `validate()` on the delegate throw an exception. + val input = JsonField.of(MESSAGE) + `when`(mockChoiceDelegate._message()).thenReturn(input) + `when`(mockChoiceDelegate.validate()).thenThrow(OpenAIInvalidDataException("test")) + val output = choiceDelegator.isValid() + + // `isValid()` calls `validate()`, which has side effects explained in its test function. + verify(mockChoiceDelegate, times(1))._message() + verify(mockChoiceDelegate, times(1)).validate() + verifyNoMoreInteractions(mockChoiceDelegate) + + assertThat(output).isFalse + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalCreateResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalCreateResponseTest.kt index 8d2a3a39..208c7d65 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalCreateResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalCreateResponseTest.kt @@ -5,6 +5,7 @@ package com.openai.models.evals import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue import com.openai.core.jsonMapper +import com.openai.models.graders.gradermodels.LabelModelGrader import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -28,12 +29,12 @@ internal class EvalCreateResponseTest { ) .name("Chatbot effectiveness Evaluation") .addTestingCriterion( - EvalLabelModelGrader.builder() + LabelModelGrader.builder() .addInput( - EvalLabelModelGrader.Input.builder() + LabelModelGrader.Input.builder() .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) .build() ) .addLabel("string") @@ -67,13 +68,13 @@ internal class EvalCreateResponseTest { assertThat(evalCreateResponse.name()).isEqualTo("Chatbot effectiveness Evaluation") assertThat(evalCreateResponse.testingCriteria()) .containsExactly( - EvalCreateResponse.TestingCriterion.ofLabelModel( - EvalLabelModelGrader.builder() + EvalCreateResponse.TestingCriterion.ofLabelModelGrader( + LabelModelGrader.builder() .addInput( - EvalLabelModelGrader.Input.builder() + LabelModelGrader.Input.builder() .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) .build() ) .addLabel("string") @@ -104,12 +105,12 @@ internal class EvalCreateResponseTest { ) .name("Chatbot effectiveness Evaluation") .addTestingCriterion( - EvalLabelModelGrader.builder() + LabelModelGrader.builder() .addInput( - EvalLabelModelGrader.Input.builder() + LabelModelGrader.Input.builder() .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) .build() ) .addLabel("string") diff --git a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalLabelModelGraderTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalLabelModelGraderTest.kt deleted file mode 100644 index 06becabb..00000000 --- a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalLabelModelGraderTest.kt +++ /dev/null @@ -1,69 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. - -package com.openai.models.evals - -import com.fasterxml.jackson.module.kotlin.jacksonTypeRef -import com.openai.core.jsonMapper -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - -internal class EvalLabelModelGraderTest { - - @Test - fun create() { - val evalLabelModelGrader = - EvalLabelModelGrader.builder() - .addInput( - EvalLabelModelGrader.Input.builder() - .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) - .build() - ) - .addLabel("string") - .model("model") - .name("name") - .addPassingLabel("string") - .build() - - assertThat(evalLabelModelGrader.input()) - .containsExactly( - EvalLabelModelGrader.Input.builder() - .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) - .build() - ) - assertThat(evalLabelModelGrader.labels()).containsExactly("string") - assertThat(evalLabelModelGrader.model()).isEqualTo("model") - assertThat(evalLabelModelGrader.name()).isEqualTo("name") - assertThat(evalLabelModelGrader.passingLabels()).containsExactly("string") - } - - @Test - fun roundtrip() { - val jsonMapper = jsonMapper() - val evalLabelModelGrader = - EvalLabelModelGrader.builder() - .addInput( - EvalLabelModelGrader.Input.builder() - .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) - .build() - ) - .addLabel("string") - .model("model") - .name("name") - .addPassingLabel("string") - .build() - - val roundtrippedEvalLabelModelGrader = - jsonMapper.readValue( - jsonMapper.writeValueAsString(evalLabelModelGrader), - jacksonTypeRef(), - ) - - assertThat(roundtrippedEvalLabelModelGrader).isEqualTo(evalLabelModelGrader) - } -} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalListPageResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalListPageResponseTest.kt index a063219c..10d5eddb 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalListPageResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalListPageResponseTest.kt @@ -5,6 +5,7 @@ package com.openai.models.evals import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue import com.openai.core.jsonMapper +import com.openai.models.graders.gradermodels.LabelModelGrader import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -30,12 +31,12 @@ internal class EvalListPageResponseTest { ) .name("Chatbot effectiveness Evaluation") .addTestingCriterion( - EvalLabelModelGrader.builder() + LabelModelGrader.builder() .addInput( - EvalLabelModelGrader.Input.builder() + LabelModelGrader.Input.builder() .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) .build() ) .addLabel("string") @@ -68,12 +69,12 @@ internal class EvalListPageResponseTest { ) .name("Chatbot effectiveness Evaluation") .addTestingCriterion( - EvalLabelModelGrader.builder() + LabelModelGrader.builder() .addInput( - EvalLabelModelGrader.Input.builder() + LabelModelGrader.Input.builder() .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) .build() ) .addLabel("string") @@ -110,12 +111,12 @@ internal class EvalListPageResponseTest { ) .name("Chatbot effectiveness Evaluation") .addTestingCriterion( - EvalLabelModelGrader.builder() + LabelModelGrader.builder() .addInput( - EvalLabelModelGrader.Input.builder() + LabelModelGrader.Input.builder() .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) .build() ) .addLabel("string") diff --git a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalListResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalListResponseTest.kt index a8835011..11f418a8 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalListResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalListResponseTest.kt @@ -5,6 +5,7 @@ package com.openai.models.evals import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue import com.openai.core.jsonMapper +import com.openai.models.graders.gradermodels.LabelModelGrader import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -28,12 +29,12 @@ internal class EvalListResponseTest { ) .name("Chatbot effectiveness Evaluation") .addTestingCriterion( - EvalLabelModelGrader.builder() + LabelModelGrader.builder() .addInput( - EvalLabelModelGrader.Input.builder() + LabelModelGrader.Input.builder() .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) .build() ) .addLabel("string") @@ -67,13 +68,13 @@ internal class EvalListResponseTest { assertThat(evalListResponse.name()).isEqualTo("Chatbot effectiveness Evaluation") assertThat(evalListResponse.testingCriteria()) .containsExactly( - EvalListResponse.TestingCriterion.ofLabelModel( - EvalLabelModelGrader.builder() + EvalListResponse.TestingCriterion.ofLabelModelGrader( + LabelModelGrader.builder() .addInput( - EvalLabelModelGrader.Input.builder() + LabelModelGrader.Input.builder() .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) .build() ) .addLabel("string") @@ -104,12 +105,12 @@ internal class EvalListResponseTest { ) .name("Chatbot effectiveness Evaluation") .addTestingCriterion( - EvalLabelModelGrader.builder() + LabelModelGrader.builder() .addInput( - EvalLabelModelGrader.Input.builder() + LabelModelGrader.Input.builder() .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) .build() ) .addLabel("string") diff --git a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalRetrieveResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalRetrieveResponseTest.kt index dcf6e03c..3838a4d0 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalRetrieveResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalRetrieveResponseTest.kt @@ -5,6 +5,7 @@ package com.openai.models.evals import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue import com.openai.core.jsonMapper +import com.openai.models.graders.gradermodels.LabelModelGrader import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -28,12 +29,12 @@ internal class EvalRetrieveResponseTest { ) .name("Chatbot effectiveness Evaluation") .addTestingCriterion( - EvalLabelModelGrader.builder() + LabelModelGrader.builder() .addInput( - EvalLabelModelGrader.Input.builder() + LabelModelGrader.Input.builder() .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) .build() ) .addLabel("string") @@ -67,13 +68,13 @@ internal class EvalRetrieveResponseTest { assertThat(evalRetrieveResponse.name()).isEqualTo("Chatbot effectiveness Evaluation") assertThat(evalRetrieveResponse.testingCriteria()) .containsExactly( - EvalRetrieveResponse.TestingCriterion.ofLabelModel( - EvalLabelModelGrader.builder() + EvalRetrieveResponse.TestingCriterion.ofLabelModelGrader( + LabelModelGrader.builder() .addInput( - EvalLabelModelGrader.Input.builder() + LabelModelGrader.Input.builder() .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) .build() ) .addLabel("string") @@ -104,12 +105,12 @@ internal class EvalRetrieveResponseTest { ) .name("Chatbot effectiveness Evaluation") .addTestingCriterion( - EvalLabelModelGrader.builder() + LabelModelGrader.builder() .addInput( - EvalLabelModelGrader.Input.builder() + LabelModelGrader.Input.builder() .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) .build() ) .addLabel("string") diff --git a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalStringCheckGraderTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalStringCheckGraderTest.kt deleted file mode 100644 index d15819ad..00000000 --- a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalStringCheckGraderTest.kt +++ /dev/null @@ -1,47 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. - -package com.openai.models.evals - -import com.fasterxml.jackson.module.kotlin.jacksonTypeRef -import com.openai.core.jsonMapper -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - -internal class EvalStringCheckGraderTest { - - @Test - fun create() { - val evalStringCheckGrader = - EvalStringCheckGrader.builder() - .input("input") - .name("name") - .operation(EvalStringCheckGrader.Operation.EQ) - .reference("reference") - .build() - - assertThat(evalStringCheckGrader.input()).isEqualTo("input") - assertThat(evalStringCheckGrader.name()).isEqualTo("name") - assertThat(evalStringCheckGrader.operation()).isEqualTo(EvalStringCheckGrader.Operation.EQ) - assertThat(evalStringCheckGrader.reference()).isEqualTo("reference") - } - - @Test - fun roundtrip() { - val jsonMapper = jsonMapper() - val evalStringCheckGrader = - EvalStringCheckGrader.builder() - .input("input") - .name("name") - .operation(EvalStringCheckGrader.Operation.EQ) - .reference("reference") - .build() - - val roundtrippedEvalStringCheckGrader = - jsonMapper.readValue( - jsonMapper.writeValueAsString(evalStringCheckGrader), - jacksonTypeRef(), - ) - - assertThat(roundtrippedEvalStringCheckGrader).isEqualTo(evalStringCheckGrader) - } -} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalTextSimilarityGraderTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalTextSimilarityGraderTest.kt deleted file mode 100644 index f7cc1230..00000000 --- a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalTextSimilarityGraderTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. - -package com.openai.models.evals - -import com.fasterxml.jackson.module.kotlin.jacksonTypeRef -import com.openai.core.jsonMapper -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - -internal class EvalTextSimilarityGraderTest { - - @Test - fun create() { - val evalTextSimilarityGrader = - EvalTextSimilarityGrader.builder() - .evaluationMetric(EvalTextSimilarityGrader.EvaluationMetric.FUZZY_MATCH) - .input("input") - .passThreshold(0.0) - .reference("reference") - .name("name") - .build() - - assertThat(evalTextSimilarityGrader.evaluationMetric()) - .isEqualTo(EvalTextSimilarityGrader.EvaluationMetric.FUZZY_MATCH) - assertThat(evalTextSimilarityGrader.input()).isEqualTo("input") - assertThat(evalTextSimilarityGrader.passThreshold()).isEqualTo(0.0) - assertThat(evalTextSimilarityGrader.reference()).isEqualTo("reference") - assertThat(evalTextSimilarityGrader.name()).contains("name") - } - - @Test - fun roundtrip() { - val jsonMapper = jsonMapper() - val evalTextSimilarityGrader = - EvalTextSimilarityGrader.builder() - .evaluationMetric(EvalTextSimilarityGrader.EvaluationMetric.FUZZY_MATCH) - .input("input") - .passThreshold(0.0) - .reference("reference") - .name("name") - .build() - - val roundtrippedEvalTextSimilarityGrader = - jsonMapper.readValue( - jsonMapper.writeValueAsString(evalTextSimilarityGrader), - jacksonTypeRef(), - ) - - assertThat(roundtrippedEvalTextSimilarityGrader).isEqualTo(evalTextSimilarityGrader) - } -} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalUpdateResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalUpdateResponseTest.kt index 6a7d1e99..e4632e7c 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalUpdateResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalUpdateResponseTest.kt @@ -5,6 +5,7 @@ package com.openai.models.evals import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue import com.openai.core.jsonMapper +import com.openai.models.graders.gradermodels.LabelModelGrader import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -28,12 +29,12 @@ internal class EvalUpdateResponseTest { ) .name("Chatbot effectiveness Evaluation") .addTestingCriterion( - EvalLabelModelGrader.builder() + LabelModelGrader.builder() .addInput( - EvalLabelModelGrader.Input.builder() + LabelModelGrader.Input.builder() .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) .build() ) .addLabel("string") @@ -67,13 +68,13 @@ internal class EvalUpdateResponseTest { assertThat(evalUpdateResponse.name()).isEqualTo("Chatbot effectiveness Evaluation") assertThat(evalUpdateResponse.testingCriteria()) .containsExactly( - EvalUpdateResponse.TestingCriterion.ofLabelModel( - EvalLabelModelGrader.builder() + EvalUpdateResponse.TestingCriterion.ofLabelModelGrader( + LabelModelGrader.builder() .addInput( - EvalLabelModelGrader.Input.builder() + LabelModelGrader.Input.builder() .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) .build() ) .addLabel("string") @@ -104,12 +105,12 @@ internal class EvalUpdateResponseTest { ) .name("Chatbot effectiveness Evaluation") .addTestingCriterion( - EvalLabelModelGrader.builder() + LabelModelGrader.builder() .addInput( - EvalLabelModelGrader.Input.builder() + LabelModelGrader.Input.builder() .content("string") - .role(EvalLabelModelGrader.Input.Role.USER) - .type(EvalLabelModelGrader.Input.Type.MESSAGE) + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) .build() ) .addLabel("string") diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunParamsTest.kt new file mode 100644 index 00000000..6054610f --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunParamsTest.kt @@ -0,0 +1,60 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.alpha.graders + +import com.openai.models.graders.gradermodels.StringCheckGrader +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class GraderRunParamsTest { + + @Test + fun create() { + GraderRunParams.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .modelSample("model_sample") + .referenceAnswer("string") + .build() + } + + @Test + fun body() { + val params = + GraderRunParams.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .modelSample("model_sample") + .referenceAnswer("string") + .build() + + val body = params._body() + + assertThat(body.grader()) + .isEqualTo( + GraderRunParams.Grader.ofStringCheck( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + ) + assertThat(body.modelSample()).isEqualTo("model_sample") + assertThat(body.referenceAnswer()) + .isEqualTo(GraderRunParams.ReferenceAnswer.ofString("string")) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunResponseTest.kt new file mode 100644 index 00000000..ee806aba --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunResponseTest.kt @@ -0,0 +1,172 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.alpha.graders + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class GraderRunResponseTest { + + @Test + fun create() { + val graderRunResponse = + GraderRunResponse.builder() + .metadata( + GraderRunResponse.Metadata.builder() + .errors( + GraderRunResponse.Metadata.Errors.builder() + .formulaParseError(true) + .invalidVariableError(true) + .modelGraderParseError(true) + .modelGraderRefusalError(true) + .modelGraderServerError(true) + .modelGraderServerErrorDetails("model_grader_server_error_details") + .otherError(true) + .pythonGraderRuntimeError(true) + .pythonGraderRuntimeErrorDetails( + "python_grader_runtime_error_details" + ) + .pythonGraderServerError(true) + .pythonGraderServerErrorType("python_grader_server_error_type") + .sampleParseError(true) + .truncatedObservationError(true) + .unresponsiveRewardError(true) + .build() + ) + .executionTime(0.0) + .name("name") + .sampledModelName("sampled_model_name") + .scores( + GraderRunResponse.Metadata.Scores.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .tokenUsage(0L) + .type("type") + .build() + ) + .modelGraderTokenUsagePerModel( + GraderRunResponse.ModelGraderTokenUsagePerModel.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .reward(0.0) + .subRewards( + GraderRunResponse.SubRewards.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + + assertThat(graderRunResponse.metadata()) + .isEqualTo( + GraderRunResponse.Metadata.builder() + .errors( + GraderRunResponse.Metadata.Errors.builder() + .formulaParseError(true) + .invalidVariableError(true) + .modelGraderParseError(true) + .modelGraderRefusalError(true) + .modelGraderServerError(true) + .modelGraderServerErrorDetails("model_grader_server_error_details") + .otherError(true) + .pythonGraderRuntimeError(true) + .pythonGraderRuntimeErrorDetails("python_grader_runtime_error_details") + .pythonGraderServerError(true) + .pythonGraderServerErrorType("python_grader_server_error_type") + .sampleParseError(true) + .truncatedObservationError(true) + .unresponsiveRewardError(true) + .build() + ) + .executionTime(0.0) + .name("name") + .sampledModelName("sampled_model_name") + .scores( + GraderRunResponse.Metadata.Scores.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .tokenUsage(0L) + .type("type") + .build() + ) + assertThat(graderRunResponse.modelGraderTokenUsagePerModel()) + .isEqualTo( + GraderRunResponse.ModelGraderTokenUsagePerModel.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + assertThat(graderRunResponse.reward()).isEqualTo(0.0) + assertThat(graderRunResponse.subRewards()) + .isEqualTo( + GraderRunResponse.SubRewards.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val graderRunResponse = + GraderRunResponse.builder() + .metadata( + GraderRunResponse.Metadata.builder() + .errors( + GraderRunResponse.Metadata.Errors.builder() + .formulaParseError(true) + .invalidVariableError(true) + .modelGraderParseError(true) + .modelGraderRefusalError(true) + .modelGraderServerError(true) + .modelGraderServerErrorDetails("model_grader_server_error_details") + .otherError(true) + .pythonGraderRuntimeError(true) + .pythonGraderRuntimeErrorDetails( + "python_grader_runtime_error_details" + ) + .pythonGraderServerError(true) + .pythonGraderServerErrorType("python_grader_server_error_type") + .sampleParseError(true) + .truncatedObservationError(true) + .unresponsiveRewardError(true) + .build() + ) + .executionTime(0.0) + .name("name") + .sampledModelName("sampled_model_name") + .scores( + GraderRunResponse.Metadata.Scores.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .tokenUsage(0L) + .type("type") + .build() + ) + .modelGraderTokenUsagePerModel( + GraderRunResponse.ModelGraderTokenUsagePerModel.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .reward(0.0) + .subRewards( + GraderRunResponse.SubRewards.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + + val roundtrippedGraderRunResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(graderRunResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedGraderRunResponse).isEqualTo(graderRunResponse) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateParamsTest.kt new file mode 100644 index 00000000..21477300 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateParamsTest.kt @@ -0,0 +1,53 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.alpha.graders + +import com.openai.models.graders.gradermodels.StringCheckGrader +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class GraderValidateParamsTest { + + @Test + fun create() { + GraderValidateParams.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .build() + } + + @Test + fun body() { + val params = + GraderValidateParams.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .build() + + val body = params._body() + + assertThat(body.grader()) + .isEqualTo( + GraderValidateParams.Grader.ofStringCheck( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + ) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateResponseTest.kt new file mode 100644 index 00000000..7fc0a06d --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateResponseTest.kt @@ -0,0 +1,63 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.alpha.graders + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import com.openai.models.graders.gradermodels.StringCheckGrader +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class GraderValidateResponseTest { + + @Test + fun create() { + val graderValidateResponse = + GraderValidateResponse.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .build() + + assertThat(graderValidateResponse.grader()) + .contains( + GraderValidateResponse.Grader.ofStringCheck( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val graderValidateResponse = + GraderValidateResponse.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .build() + + val roundtrippedGraderValidateResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(graderValidateResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedGraderValidateResponse).isEqualTo(graderValidateResponse) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobTest.kt index 33c530e6..0d115005 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/FineTuningJobTest.kt @@ -5,6 +5,13 @@ package com.openai.models.finetuning.jobs import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue import com.openai.core.jsonMapper +import com.openai.models.finetuning.methods.DpoHyperparameters +import com.openai.models.finetuning.methods.DpoMethod +import com.openai.models.finetuning.methods.ReinforcementHyperparameters +import com.openai.models.finetuning.methods.ReinforcementMethod +import com.openai.models.finetuning.methods.SupervisedHyperparameters +import com.openai.models.finetuning.methods.SupervisedMethod +import com.openai.models.graders.gradermodels.StringCheckGrader import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -28,7 +35,7 @@ internal class FineTuningJobTest { .finishedAt(0L) .hyperparameters( FineTuningJob.Hyperparameters.builder() - .batchSizeAuto() + .batchSize(JsonValue.from(mapOf())) .learningRateMultiplierAuto() .nEpochsAuto() .build() @@ -61,10 +68,11 @@ internal class FineTuningJobTest { ) .method( FineTuningJob.Method.builder() + .type(FineTuningJob.Method.Type.SUPERVISED) .dpo( - FineTuningJob.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - FineTuningJob.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -73,10 +81,35 @@ internal class FineTuningJobTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort.DEFAULT + ) + .build() + ) + .build() + ) .supervised( - FineTuningJob.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - FineTuningJob.Method.Supervised.Hyperparameters.builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -84,7 +117,6 @@ internal class FineTuningJobTest { ) .build() ) - .type(FineTuningJob.Method.Type.SUPERVISED) .build() ) .build() @@ -100,7 +132,7 @@ internal class FineTuningJobTest { assertThat(fineTuningJob.hyperparameters()) .isEqualTo( FineTuningJob.Hyperparameters.builder() - .batchSizeAuto() + .batchSize(JsonValue.from(mapOf())) .learningRateMultiplierAuto() .nEpochsAuto() .build() @@ -136,10 +168,11 @@ internal class FineTuningJobTest { assertThat(fineTuningJob.method()) .contains( FineTuningJob.Method.builder() + .type(FineTuningJob.Method.Type.SUPERVISED) .dpo( - FineTuningJob.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - FineTuningJob.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -148,10 +181,35 @@ internal class FineTuningJobTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort.DEFAULT + ) + .build() + ) + .build() + ) .supervised( - FineTuningJob.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - FineTuningJob.Method.Supervised.Hyperparameters.builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -159,7 +217,6 @@ internal class FineTuningJobTest { ) .build() ) - .type(FineTuningJob.Method.Type.SUPERVISED) .build() ) } @@ -182,7 +239,7 @@ internal class FineTuningJobTest { .finishedAt(0L) .hyperparameters( FineTuningJob.Hyperparameters.builder() - .batchSizeAuto() + .batchSize(JsonValue.from(mapOf())) .learningRateMultiplierAuto() .nEpochsAuto() .build() @@ -215,10 +272,11 @@ internal class FineTuningJobTest { ) .method( FineTuningJob.Method.builder() + .type(FineTuningJob.Method.Type.SUPERVISED) .dpo( - FineTuningJob.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - FineTuningJob.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -227,10 +285,35 @@ internal class FineTuningJobTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort.DEFAULT + ) + .build() + ) + .build() + ) .supervised( - FineTuningJob.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - FineTuningJob.Method.Supervised.Hyperparameters.builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -238,7 +321,6 @@ internal class FineTuningJobTest { ) .build() ) - .type(FineTuningJob.Method.Type.SUPERVISED) .build() ) .build() diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobCreateParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobCreateParamsTest.kt index f3391493..28cd1a8b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobCreateParamsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobCreateParamsTest.kt @@ -3,6 +3,13 @@ package com.openai.models.finetuning.jobs import com.openai.core.JsonValue +import com.openai.models.finetuning.methods.DpoHyperparameters +import com.openai.models.finetuning.methods.DpoMethod +import com.openai.models.finetuning.methods.ReinforcementHyperparameters +import com.openai.models.finetuning.methods.ReinforcementMethod +import com.openai.models.finetuning.methods.SupervisedHyperparameters +import com.openai.models.finetuning.methods.SupervisedMethod +import com.openai.models.graders.gradermodels.StringCheckGrader import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -40,10 +47,11 @@ internal class JobCreateParamsTest { ) .method( JobCreateParams.Method.builder() + .type(JobCreateParams.Method.Type.SUPERVISED) .dpo( - JobCreateParams.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - JobCreateParams.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -52,10 +60,35 @@ internal class JobCreateParamsTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort.DEFAULT + ) + .build() + ) + .build() + ) .supervised( - JobCreateParams.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - JobCreateParams.Method.Supervised.Hyperparameters.builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -63,7 +96,6 @@ internal class JobCreateParamsTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) .seed(42L) @@ -104,10 +136,11 @@ internal class JobCreateParamsTest { ) .method( JobCreateParams.Method.builder() + .type(JobCreateParams.Method.Type.SUPERVISED) .dpo( - JobCreateParams.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - JobCreateParams.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -116,10 +149,35 @@ internal class JobCreateParamsTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort.DEFAULT + ) + .build() + ) + .build() + ) .supervised( - JobCreateParams.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - JobCreateParams.Method.Supervised.Hyperparameters.builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -127,7 +185,6 @@ internal class JobCreateParamsTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) .seed(42L) @@ -169,10 +226,11 @@ internal class JobCreateParamsTest { assertThat(body.method()) .contains( JobCreateParams.Method.builder() + .type(JobCreateParams.Method.Type.SUPERVISED) .dpo( - JobCreateParams.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - JobCreateParams.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -181,10 +239,35 @@ internal class JobCreateParamsTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort.DEFAULT + ) + .build() + ) + .build() + ) .supervised( - JobCreateParams.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - JobCreateParams.Method.Supervised.Hyperparameters.builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -192,7 +275,6 @@ internal class JobCreateParamsTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) assertThat(body.seed()).contains(42L) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobListPageResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobListPageResponseTest.kt index 833580a8..40fa837b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobListPageResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobListPageResponseTest.kt @@ -5,6 +5,13 @@ package com.openai.models.finetuning.jobs import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import com.openai.core.JsonValue import com.openai.core.jsonMapper +import com.openai.models.finetuning.methods.DpoHyperparameters +import com.openai.models.finetuning.methods.DpoMethod +import com.openai.models.finetuning.methods.ReinforcementHyperparameters +import com.openai.models.finetuning.methods.ReinforcementMethod +import com.openai.models.finetuning.methods.SupervisedHyperparameters +import com.openai.models.finetuning.methods.SupervisedMethod +import com.openai.models.graders.gradermodels.StringCheckGrader import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -29,7 +36,7 @@ internal class JobListPageResponseTest { .finishedAt(0L) .hyperparameters( FineTuningJob.Hyperparameters.builder() - .batchSizeAuto() + .batchSize(JsonValue.from(mapOf())) .learningRateMultiplierAuto() .nEpochsAuto() .build() @@ -62,10 +69,11 @@ internal class JobListPageResponseTest { ) .method( FineTuningJob.Method.builder() + .type(FineTuningJob.Method.Type.SUPERVISED) .dpo( - FineTuningJob.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - FineTuningJob.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -74,11 +82,36 @@ internal class JobListPageResponseTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort + .DEFAULT + ) + .build() + ) + .build() + ) .supervised( - FineTuningJob.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - FineTuningJob.Method.Supervised.Hyperparameters - .builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -86,7 +119,6 @@ internal class JobListPageResponseTest { ) .build() ) - .type(FineTuningJob.Method.Type.SUPERVISED) .build() ) .build() @@ -110,7 +142,7 @@ internal class JobListPageResponseTest { .finishedAt(0L) .hyperparameters( FineTuningJob.Hyperparameters.builder() - .batchSizeAuto() + .batchSize(JsonValue.from(mapOf())) .learningRateMultiplierAuto() .nEpochsAuto() .build() @@ -143,10 +175,11 @@ internal class JobListPageResponseTest { ) .method( FineTuningJob.Method.builder() + .type(FineTuningJob.Method.Type.SUPERVISED) .dpo( - FineTuningJob.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - FineTuningJob.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -155,10 +188,35 @@ internal class JobListPageResponseTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort.DEFAULT + ) + .build() + ) + .build() + ) .supervised( - FineTuningJob.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - FineTuningJob.Method.Supervised.Hyperparameters.builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -166,7 +224,6 @@ internal class JobListPageResponseTest { ) .build() ) - .type(FineTuningJob.Method.Type.SUPERVISED) .build() ) .build() @@ -194,7 +251,7 @@ internal class JobListPageResponseTest { .finishedAt(0L) .hyperparameters( FineTuningJob.Hyperparameters.builder() - .batchSizeAuto() + .batchSize(JsonValue.from(mapOf())) .learningRateMultiplierAuto() .nEpochsAuto() .build() @@ -227,10 +284,11 @@ internal class JobListPageResponseTest { ) .method( FineTuningJob.Method.builder() + .type(FineTuningJob.Method.Type.SUPERVISED) .dpo( - FineTuningJob.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - FineTuningJob.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -239,11 +297,36 @@ internal class JobListPageResponseTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort + .DEFAULT + ) + .build() + ) + .build() + ) .supervised( - FineTuningJob.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - FineTuningJob.Method.Supervised.Hyperparameters - .builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -251,7 +334,6 @@ internal class JobListPageResponseTest { ) .build() ) - .type(FineTuningJob.Method.Type.SUPERVISED) .build() ) .build() diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobPauseParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobPauseParamsTest.kt new file mode 100644 index 00000000..052bab3e --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobPauseParamsTest.kt @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.jobs + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class JobPauseParamsTest { + + @Test + fun create() { + JobPauseParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() + } + + @Test + fun pathParams() { + val params = JobPauseParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() + + assertThat(params._pathParam(0)).isEqualTo("ft-AF1WoRqd3aJAHsqc9NY7iL8F") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobResumeParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobResumeParamsTest.kt new file mode 100644 index 00000000..e3f90315 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobResumeParamsTest.kt @@ -0,0 +1,24 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.jobs + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class JobResumeParamsTest { + + @Test + fun create() { + JobResumeParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() + } + + @Test + fun pathParams() { + val params = + JobResumeParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() + + assertThat(params._pathParam(0)).isEqualTo("ft-AF1WoRqd3aJAHsqc9NY7iL8F") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/DpoHyperparametersTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/DpoHyperparametersTest.kt new file mode 100644 index 00000000..d220c3c7 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/DpoHyperparametersTest.kt @@ -0,0 +1,48 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.methods + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class DpoHyperparametersTest { + + @Test + fun create() { + val dpoHyperparameters = + DpoHyperparameters.builder() + .batchSizeAuto() + .betaAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + + assertThat(dpoHyperparameters.batchSize()).contains(DpoHyperparameters.BatchSize.ofAuto()) + assertThat(dpoHyperparameters.beta()).contains(DpoHyperparameters.Beta.ofAuto()) + assertThat(dpoHyperparameters.learningRateMultiplier()) + .contains(DpoHyperparameters.LearningRateMultiplier.ofAuto()) + assertThat(dpoHyperparameters.nEpochs()).contains(DpoHyperparameters.NEpochs.ofAuto()) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val dpoHyperparameters = + DpoHyperparameters.builder() + .batchSizeAuto() + .betaAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + + val roundtrippedDpoHyperparameters = + jsonMapper.readValue( + jsonMapper.writeValueAsString(dpoHyperparameters), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDpoHyperparameters).isEqualTo(dpoHyperparameters) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/DpoMethodTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/DpoMethodTest.kt new file mode 100644 index 00000000..58ef8731 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/DpoMethodTest.kt @@ -0,0 +1,60 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.methods + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class DpoMethodTest { + + @Test + fun create() { + val dpoMethod = + DpoMethod.builder() + .hyperparameters( + DpoHyperparameters.builder() + .batchSizeAuto() + .betaAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .build() + + assertThat(dpoMethod.hyperparameters()) + .contains( + DpoHyperparameters.builder() + .batchSizeAuto() + .betaAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val dpoMethod = + DpoMethod.builder() + .hyperparameters( + DpoHyperparameters.builder() + .batchSizeAuto() + .betaAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .build() + + val roundtrippedDpoMethod = + jsonMapper.readValue( + jsonMapper.writeValueAsString(dpoMethod), + jacksonTypeRef(), + ) + + assertThat(roundtrippedDpoMethod).isEqualTo(dpoMethod) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/ReinforcementHyperparametersTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/ReinforcementHyperparametersTest.kt new file mode 100644 index 00000000..c7204234 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/ReinforcementHyperparametersTest.kt @@ -0,0 +1,63 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.methods + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ReinforcementHyperparametersTest { + + @Test + fun create() { + val reinforcementHyperparameters = + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort(ReinforcementHyperparameters.ReasoningEffort.DEFAULT) + .build() + + assertThat(reinforcementHyperparameters.batchSize()) + .contains(ReinforcementHyperparameters.BatchSize.ofAuto()) + assertThat(reinforcementHyperparameters.computeMultiplier()) + .contains(ReinforcementHyperparameters.ComputeMultiplier.ofAuto()) + assertThat(reinforcementHyperparameters.evalInterval()) + .contains(ReinforcementHyperparameters.EvalInterval.ofAuto()) + assertThat(reinforcementHyperparameters.evalSamples()) + .contains(ReinforcementHyperparameters.EvalSamples.ofAuto()) + assertThat(reinforcementHyperparameters.learningRateMultiplier()) + .contains(ReinforcementHyperparameters.LearningRateMultiplier.ofAuto()) + assertThat(reinforcementHyperparameters.nEpochs()) + .contains(ReinforcementHyperparameters.NEpochs.ofAuto()) + assertThat(reinforcementHyperparameters.reasoningEffort()) + .contains(ReinforcementHyperparameters.ReasoningEffort.DEFAULT) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val reinforcementHyperparameters = + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort(ReinforcementHyperparameters.ReasoningEffort.DEFAULT) + .build() + + val roundtrippedReinforcementHyperparameters = + jsonMapper.readValue( + jsonMapper.writeValueAsString(reinforcementHyperparameters), + jacksonTypeRef(), + ) + + assertThat(roundtrippedReinforcementHyperparameters).isEqualTo(reinforcementHyperparameters) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/ReinforcementMethodTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/ReinforcementMethodTest.kt new file mode 100644 index 00000000..271a02be --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/ReinforcementMethodTest.kt @@ -0,0 +1,97 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.methods + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import com.openai.models.graders.gradermodels.StringCheckGrader +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ReinforcementMethodTest { + + @Test + fun create() { + val reinforcementMethod = + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort(ReinforcementHyperparameters.ReasoningEffort.DEFAULT) + .build() + ) + .build() + + assertThat(reinforcementMethod.grader()) + .isEqualTo( + ReinforcementMethod.Grader.ofStringCheck( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + ) + assertThat(reinforcementMethod.hyperparameters()) + .contains( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort(ReinforcementHyperparameters.ReasoningEffort.DEFAULT) + .build() + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val reinforcementMethod = + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort(ReinforcementHyperparameters.ReasoningEffort.DEFAULT) + .build() + ) + .build() + + val roundtrippedReinforcementMethod = + jsonMapper.readValue( + jsonMapper.writeValueAsString(reinforcementMethod), + jacksonTypeRef(), + ) + + assertThat(roundtrippedReinforcementMethod).isEqualTo(reinforcementMethod) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/SupervisedHyperparametersTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/SupervisedHyperparametersTest.kt new file mode 100644 index 00000000..99b9ec04 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/SupervisedHyperparametersTest.kt @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.methods + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class SupervisedHyperparametersTest { + + @Test + fun create() { + val supervisedHyperparameters = + SupervisedHyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + + assertThat(supervisedHyperparameters.batchSize()) + .contains(SupervisedHyperparameters.BatchSize.ofAuto()) + assertThat(supervisedHyperparameters.learningRateMultiplier()) + .contains(SupervisedHyperparameters.LearningRateMultiplier.ofAuto()) + assertThat(supervisedHyperparameters.nEpochs()) + .contains(SupervisedHyperparameters.NEpochs.ofAuto()) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val supervisedHyperparameters = + SupervisedHyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + + val roundtrippedSupervisedHyperparameters = + jsonMapper.readValue( + jsonMapper.writeValueAsString(supervisedHyperparameters), + jacksonTypeRef(), + ) + + assertThat(roundtrippedSupervisedHyperparameters).isEqualTo(supervisedHyperparameters) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/SupervisedMethodTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/SupervisedMethodTest.kt new file mode 100644 index 00000000..93769d44 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/SupervisedMethodTest.kt @@ -0,0 +1,57 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.finetuning.methods + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class SupervisedMethodTest { + + @Test + fun create() { + val supervisedMethod = + SupervisedMethod.builder() + .hyperparameters( + SupervisedHyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .build() + + assertThat(supervisedMethod.hyperparameters()) + .contains( + SupervisedHyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val supervisedMethod = + SupervisedMethod.builder() + .hyperparameters( + SupervisedHyperparameters.builder() + .batchSizeAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .build() + ) + .build() + + val roundtrippedSupervisedMethod = + jsonMapper.readValue( + jsonMapper.writeValueAsString(supervisedMethod), + jacksonTypeRef(), + ) + + assertThat(roundtrippedSupervisedMethod).isEqualTo(supervisedMethod) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/LabelModelGraderTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/LabelModelGraderTest.kt new file mode 100644 index 00000000..8243d736 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/LabelModelGraderTest.kt @@ -0,0 +1,69 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.graders.gradermodels + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class LabelModelGraderTest { + + @Test + fun create() { + val labelModelGrader = + LabelModelGrader.builder() + .addInput( + LabelModelGrader.Input.builder() + .content("string") + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) + .build() + ) + .addLabel("string") + .model("model") + .name("name") + .addPassingLabel("string") + .build() + + assertThat(labelModelGrader.input()) + .containsExactly( + LabelModelGrader.Input.builder() + .content("string") + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) + .build() + ) + assertThat(labelModelGrader.labels()).containsExactly("string") + assertThat(labelModelGrader.model()).isEqualTo("model") + assertThat(labelModelGrader.name()).isEqualTo("name") + assertThat(labelModelGrader.passingLabels()).containsExactly("string") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val labelModelGrader = + LabelModelGrader.builder() + .addInput( + LabelModelGrader.Input.builder() + .content("string") + .role(LabelModelGrader.Input.Role.USER) + .type(LabelModelGrader.Input.Type.MESSAGE) + .build() + ) + .addLabel("string") + .model("model") + .name("name") + .addPassingLabel("string") + .build() + + val roundtrippedLabelModelGrader = + jsonMapper.readValue( + jsonMapper.writeValueAsString(labelModelGrader), + jacksonTypeRef(), + ) + + assertThat(roundtrippedLabelModelGrader).isEqualTo(labelModelGrader) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/MultiGraderTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/MultiGraderTest.kt new file mode 100644 index 00000000..81423d03 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/MultiGraderTest.kt @@ -0,0 +1,91 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.graders.gradermodels + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class MultiGraderTest { + + @Test + fun create() { + val multiGrader = + MultiGrader.builder() + .calculateOutput("calculate_output") + .graders( + MultiGrader.Graders.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf( + "input" to "input", + "name" to "name", + "operation" to "eq", + "reference" to "reference", + "type" to "string_check", + ) + ), + ) + .build() + ) + .name("name") + .build() + + assertThat(multiGrader.calculateOutput()).isEqualTo("calculate_output") + assertThat(multiGrader.graders()) + .isEqualTo( + MultiGrader.Graders.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf( + "input" to "input", + "name" to "name", + "operation" to "eq", + "reference" to "reference", + "type" to "string_check", + ) + ), + ) + .build() + ) + assertThat(multiGrader.name()).isEqualTo("name") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val multiGrader = + MultiGrader.builder() + .calculateOutput("calculate_output") + .graders( + MultiGrader.Graders.builder() + .putAdditionalProperty( + "foo", + JsonValue.from( + mapOf( + "input" to "input", + "name" to "name", + "operation" to "eq", + "reference" to "reference", + "type" to "string_check", + ) + ), + ) + .build() + ) + .name("name") + .build() + + val roundtrippedMultiGrader = + jsonMapper.readValue( + jsonMapper.writeValueAsString(multiGrader), + jacksonTypeRef(), + ) + + assertThat(roundtrippedMultiGrader).isEqualTo(multiGrader) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/PythonGraderTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/PythonGraderTest.kt new file mode 100644 index 00000000..3ac30afb --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/PythonGraderTest.kt @@ -0,0 +1,36 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.graders.gradermodels + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class PythonGraderTest { + + @Test + fun create() { + val pythonGrader = + PythonGrader.builder().name("name").source("source").imageTag("image_tag").build() + + assertThat(pythonGrader.name()).isEqualTo("name") + assertThat(pythonGrader.source()).isEqualTo("source") + assertThat(pythonGrader.imageTag()).contains("image_tag") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val pythonGrader = + PythonGrader.builder().name("name").source("source").imageTag("image_tag").build() + + val roundtrippedPythonGrader = + jsonMapper.readValue( + jsonMapper.writeValueAsString(pythonGrader), + jacksonTypeRef(), + ) + + assertThat(roundtrippedPythonGrader).isEqualTo(pythonGrader) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/ScoreModelGraderTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/ScoreModelGraderTest.kt new file mode 100644 index 00000000..ff2134b6 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/ScoreModelGraderTest.kt @@ -0,0 +1,72 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.graders.gradermodels + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import kotlin.jvm.optionals.getOrNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ScoreModelGraderTest { + + @Test + fun create() { + val scoreModelGrader = + ScoreModelGrader.builder() + .addInput( + ScoreModelGrader.Input.builder() + .content("string") + .role(ScoreModelGrader.Input.Role.USER) + .type(ScoreModelGrader.Input.Type.MESSAGE) + .build() + ) + .model("model") + .name("name") + .addRange(0.0) + .samplingParams(JsonValue.from(mapOf())) + .build() + + assertThat(scoreModelGrader.input()) + .containsExactly( + ScoreModelGrader.Input.builder() + .content("string") + .role(ScoreModelGrader.Input.Role.USER) + .type(ScoreModelGrader.Input.Type.MESSAGE) + .build() + ) + assertThat(scoreModelGrader.model()).isEqualTo("model") + assertThat(scoreModelGrader.name()).isEqualTo("name") + assertThat(scoreModelGrader.range().getOrNull()).containsExactly(0.0) + assertThat(scoreModelGrader._samplingParams()) + .isEqualTo(JsonValue.from(mapOf())) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val scoreModelGrader = + ScoreModelGrader.builder() + .addInput( + ScoreModelGrader.Input.builder() + .content("string") + .role(ScoreModelGrader.Input.Role.USER) + .type(ScoreModelGrader.Input.Type.MESSAGE) + .build() + ) + .model("model") + .name("name") + .addRange(0.0) + .samplingParams(JsonValue.from(mapOf())) + .build() + + val roundtrippedScoreModelGrader = + jsonMapper.readValue( + jsonMapper.writeValueAsString(scoreModelGrader), + jacksonTypeRef(), + ) + + assertThat(roundtrippedScoreModelGrader).isEqualTo(scoreModelGrader) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/StringCheckGraderTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/StringCheckGraderTest.kt new file mode 100644 index 00000000..6eb0c92b --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/StringCheckGraderTest.kt @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.graders.gradermodels + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class StringCheckGraderTest { + + @Test + fun create() { + val stringCheckGrader = + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + + assertThat(stringCheckGrader.input()).isEqualTo("input") + assertThat(stringCheckGrader.name()).isEqualTo("name") + assertThat(stringCheckGrader.operation()).isEqualTo(StringCheckGrader.Operation.EQ) + assertThat(stringCheckGrader.reference()).isEqualTo("reference") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val stringCheckGrader = + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + + val roundtrippedStringCheckGrader = + jsonMapper.readValue( + jsonMapper.writeValueAsString(stringCheckGrader), + jacksonTypeRef(), + ) + + assertThat(roundtrippedStringCheckGrader).isEqualTo(stringCheckGrader) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/TextSimilarityGraderTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/TextSimilarityGraderTest.kt new file mode 100644 index 00000000..f271eeb4 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/TextSimilarityGraderTest.kt @@ -0,0 +1,48 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.graders.gradermodels + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class TextSimilarityGraderTest { + + @Test + fun create() { + val textSimilarityGrader = + TextSimilarityGrader.builder() + .evaluationMetric(TextSimilarityGrader.EvaluationMetric.FUZZY_MATCH) + .input("input") + .name("name") + .reference("reference") + .build() + + assertThat(textSimilarityGrader.evaluationMetric()) + .isEqualTo(TextSimilarityGrader.EvaluationMetric.FUZZY_MATCH) + assertThat(textSimilarityGrader.input()).isEqualTo("input") + assertThat(textSimilarityGrader.name()).isEqualTo("name") + assertThat(textSimilarityGrader.reference()).isEqualTo("reference") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val textSimilarityGrader = + TextSimilarityGrader.builder() + .evaluationMetric(TextSimilarityGrader.EvaluationMetric.FUZZY_MATCH) + .input("input") + .name("name") + .reference("reference") + .build() + + val roundtrippedTextSimilarityGrader = + jsonMapper.readValue( + jsonMapper.writeValueAsString(textSimilarityGrader), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTextSimilarityGrader).isEqualTo(textSimilarityGrader) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchToolCallTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchToolCallTest.kt index 50752fd3..414ef927 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchToolCallTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchToolCallTest.kt @@ -27,7 +27,7 @@ internal class ResponseFileSearchToolCallTest { ) .fileId("file_id") .filename("filename") - .score(0.0) + .score(0.0f) .text("text") .build() ) @@ -47,7 +47,7 @@ internal class ResponseFileSearchToolCallTest { ) .fileId("file_id") .filename("filename") - .score(0.0) + .score(0.0f) .text("text") .build() ) @@ -70,7 +70,7 @@ internal class ResponseFileSearchToolCallTest { ) .fileId("file_id") .filename("filename") - .score(0.0) + .score(0.0f) .text("text") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt index 4ca8296c..875e90e4 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt @@ -187,7 +187,7 @@ internal class ResponseInputItemTest { ) .fileId("file_id") .filename("filename") - .score(0.0) + .score(0.0f) .text("text") .build() ) @@ -226,7 +226,7 @@ internal class ResponseInputItemTest { ) .fileId("file_id") .filename("filename") - .score(0.0) + .score(0.0f) .text("text") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseItemTest.kt index 2e4bfed3..2f3896e8 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseItemTest.kt @@ -138,7 +138,7 @@ internal class ResponseItemTest { ) .fileId("file_id") .filename("filename") - .score(0.0) + .score(0.0f) .text("text") .build() ) @@ -174,7 +174,7 @@ internal class ResponseItemTest { ) .fileId("file_id") .filename("filename") - .score(0.0) + .score(0.0f) .text("text") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemTest.kt index 763f097a..66edbe7d 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseOutputItemTest.kt @@ -90,7 +90,7 @@ internal class ResponseOutputItemTest { ) .fileId("file_id") .filename("filename") - .score(0.0) + .score(0.0f) .text("text") .build() ) @@ -124,7 +124,7 @@ internal class ResponseOutputItemTest { ) .fileId("file_id") .filename("filename") - .score(0.0) + .score(0.0f) .text("text") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseCreateParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseCreateParamsTest.kt new file mode 100644 index 00000000..3d205072 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseCreateParamsTest.kt @@ -0,0 +1,243 @@ +package com.openai.models.responses + +import com.openai.core.BOOLEAN +import com.openai.core.DOUBLE +import com.openai.core.DelegationWriteTestCase +import com.openai.core.JSON_FIELD +import com.openai.core.JSON_VALUE +import com.openai.core.LIST +import com.openai.core.LONG +import com.openai.core.MAP +import com.openai.core.NULLABLE +import com.openai.core.NULLABLE_BOOLEAN +import com.openai.core.NULLABLE_DOUBLE +import com.openai.core.NULLABLE_LONG +import com.openai.core.NULLABLE_STRING +import com.openai.core.OPTIONAL +import com.openai.core.SET +import com.openai.core.STRING +import com.openai.core.X +import com.openai.core.checkAllDelegation +import com.openai.core.checkAllDelegatorWriteFunctionsAreTested +import com.openai.core.checkOneDelegationWrite +import com.openai.core.findDelegationMethod +import com.openai.core.http.Headers +import com.openai.core.http.QueryParams +import com.openai.core.textConfigFromClass +import com.openai.models.ChatModel +import com.openai.models.Reasoning +import com.openai.models.ResponsesModel +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import org.mockito.Mockito.mock +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.kotlin.times +import org.mockito.kotlin.verify + +/** + * Unit tests for the [StructuredResponseCreateParams] class (delegator) and its delegation of most + * functions to a wrapped [ResponseCreateParams] (delegate). The tests include confirmation of the + * following: + * - All functions in the delegator correspond to a function in the delegate and _vice versa_. + * - All functions in the delegator call their corresponding function in the delegate and only that + * function. + * - A unit test exists for all functions. + * + * There are some exceptions to the above that are handled differently. + */ +internal class StructuredResponseCreateParamsTest { + companion object { + private val CHAT_MODEL = ChatModel.GPT_4O + private val RESPONSES_MODEL = ResponsesModel.ofChat(CHAT_MODEL) + private val RESPONSES_ONLY_MODEL = ResponsesModel.ResponsesOnlyModel.O1_PRO + private val PARAMS_INPUT = ResponseCreateParams.Input.ofText(STRING) + + private val INCLUDABLE = ResponseIncludable.of(STRING) + private val METADATA = ResponseCreateParams.Metadata.builder().build() + private val SERVICE_TIER = ResponseCreateParams.ServiceTier.AUTO + private val REASONING = Reasoning.builder().build() + + private val TOOL_CHOICE_TYPE = ToolChoiceTypes.Type.FILE_SEARCH + private val TOOL_CHOICE_TYPES = ToolChoiceTypes.builder().type(TOOL_CHOICE_TYPE).build() + private val TOOL_CHOICE = ResponseCreateParams.ToolChoice.ofTypes(TOOL_CHOICE_TYPES) + private val TOOL_CHOICE_OPTIONS = ToolChoiceOptions.AUTO + private val TOOL_CHOICE_FUNCTION = ToolChoiceFunction.builder().name(STRING).build() + + private val FUNCTION_TOOL = + FunctionTool.builder().name(STRING).parameters(NULLABLE).strict(BOOLEAN).build() + private val FILE_SEARCH_TOOL = FileSearchTool.builder().vectorStoreIds(LIST).build() + private val WEB_SEARCH_TOOL = + WebSearchTool.builder().type(WebSearchTool.Type.WEB_SEARCH_PREVIEW).build() + private val COMPUTER_TOOL = + ComputerTool.builder() + .displayWidth(LONG) + .displayHeight(LONG) + .environment(ComputerTool.Environment.LINUX) + .build() + private val TOOL = Tool.ofFunction(FUNCTION_TOOL) + + private val HEADERS = Headers.builder().build() + private val QUERY_PARAMS = QueryParams.builder().build() + + // The list order follows the declaration order in `ResponseCreateParams.Builder` for + // easier maintenance. + @JvmStatic + private fun builderDelegationTestCases() = + listOf( + // The `body(...)` function is deliberately not supported: too messy. + DelegationWriteTestCase("input", PARAMS_INPUT), + DelegationWriteTestCase("input", JSON_FIELD), + DelegationWriteTestCase("input", STRING), + DelegationWriteTestCase("inputOfResponse", LIST), + DelegationWriteTestCase("model", RESPONSES_MODEL), + DelegationWriteTestCase("model", JSON_FIELD), + DelegationWriteTestCase("model", STRING), + DelegationWriteTestCase("model", CHAT_MODEL), + DelegationWriteTestCase("model", RESPONSES_ONLY_MODEL), + DelegationWriteTestCase("include", LIST), + DelegationWriteTestCase("include", OPTIONAL), + DelegationWriteTestCase("include", JSON_FIELD), + DelegationWriteTestCase("addInclude", INCLUDABLE), + DelegationWriteTestCase("instructions", NULLABLE_STRING), + DelegationWriteTestCase("instructions", OPTIONAL), + DelegationWriteTestCase("instructions", JSON_FIELD), + DelegationWriteTestCase("maxOutputTokens", NULLABLE_LONG), + DelegationWriteTestCase("maxOutputTokens", LONG), + DelegationWriteTestCase("maxOutputTokens", OPTIONAL), + DelegationWriteTestCase("maxOutputTokens", JSON_FIELD), + DelegationWriteTestCase("metadata", METADATA), + DelegationWriteTestCase("metadata", OPTIONAL), + DelegationWriteTestCase("metadata", JSON_FIELD), + DelegationWriteTestCase("parallelToolCalls", NULLABLE_BOOLEAN), + DelegationWriteTestCase("parallelToolCalls", BOOLEAN), + DelegationWriteTestCase("parallelToolCalls", OPTIONAL), + DelegationWriteTestCase("parallelToolCalls", JSON_FIELD), + DelegationWriteTestCase("previousResponseId", NULLABLE_STRING), + DelegationWriteTestCase("previousResponseId", OPTIONAL), + DelegationWriteTestCase("previousResponseId", JSON_FIELD), + DelegationWriteTestCase("reasoning", REASONING), + DelegationWriteTestCase("reasoning", OPTIONAL), + DelegationWriteTestCase("reasoning", JSON_FIELD), + DelegationWriteTestCase("serviceTier", SERVICE_TIER), + DelegationWriteTestCase("serviceTier", OPTIONAL), + DelegationWriteTestCase("serviceTier", JSON_FIELD), + DelegationWriteTestCase("store", NULLABLE_BOOLEAN), + DelegationWriteTestCase("store", BOOLEAN), + DelegationWriteTestCase("store", OPTIONAL), + DelegationWriteTestCase("store", JSON_FIELD), + DelegationWriteTestCase("temperature", NULLABLE_DOUBLE), + DelegationWriteTestCase("temperature", DOUBLE), + DelegationWriteTestCase("temperature", OPTIONAL), + DelegationWriteTestCase("temperature", JSON_FIELD), + // `text()` is a special case and has its own unit tests. + DelegationWriteTestCase("toolChoice", TOOL_CHOICE), + DelegationWriteTestCase("toolChoice", JSON_FIELD), + DelegationWriteTestCase("toolChoice", TOOL_CHOICE_OPTIONS), + DelegationWriteTestCase("toolChoice", TOOL_CHOICE_TYPES), + DelegationWriteTestCase("toolChoice", TOOL_CHOICE_FUNCTION), + DelegationWriteTestCase("tools", LIST), + DelegationWriteTestCase("tools", JSON_FIELD), + DelegationWriteTestCase("addTool", TOOL), + DelegationWriteTestCase("addTool", FILE_SEARCH_TOOL), + DelegationWriteTestCase("addFileSearchTool", LIST), + DelegationWriteTestCase("addTool", FUNCTION_TOOL), + DelegationWriteTestCase("addTool", WEB_SEARCH_TOOL), + DelegationWriteTestCase("addTool", COMPUTER_TOOL), + DelegationWriteTestCase("topP", NULLABLE_DOUBLE), + DelegationWriteTestCase("topP", DOUBLE), + DelegationWriteTestCase("topP", OPTIONAL), + DelegationWriteTestCase("topP", JSON_FIELD), + DelegationWriteTestCase("truncation", NULLABLE), + DelegationWriteTestCase("truncation", OPTIONAL), + DelegationWriteTestCase("truncation", JSON_FIELD), + DelegationWriteTestCase("user", STRING), + DelegationWriteTestCase("user", JSON_FIELD), + DelegationWriteTestCase("additionalBodyProperties", MAP), + DelegationWriteTestCase("putAdditionalBodyProperty", STRING, JSON_VALUE), + DelegationWriteTestCase("putAllAdditionalBodyProperties", MAP), + DelegationWriteTestCase("removeAdditionalBodyProperty", STRING), + DelegationWriteTestCase("removeAllAdditionalBodyProperties", SET), + DelegationWriteTestCase("additionalHeaders", HEADERS), + DelegationWriteTestCase("additionalHeaders", MAP), + DelegationWriteTestCase("putAdditionalHeader", STRING, STRING), + DelegationWriteTestCase("putAdditionalHeaders", STRING, LIST), + DelegationWriteTestCase("putAllAdditionalHeaders", HEADERS), + DelegationWriteTestCase("putAllAdditionalHeaders", MAP), + DelegationWriteTestCase("replaceAdditionalHeaders", STRING, STRING), + DelegationWriteTestCase("replaceAdditionalHeaders", STRING, LIST), + DelegationWriteTestCase("replaceAllAdditionalHeaders", HEADERS), + DelegationWriteTestCase("replaceAllAdditionalHeaders", MAP), + DelegationWriteTestCase("removeAdditionalHeaders", STRING), + DelegationWriteTestCase("removeAllAdditionalHeaders", SET), + DelegationWriteTestCase("additionalQueryParams", QUERY_PARAMS), + DelegationWriteTestCase("additionalQueryParams", MAP), + DelegationWriteTestCase("putAdditionalQueryParam", STRING, STRING), + DelegationWriteTestCase("putAdditionalQueryParams", STRING, LIST), + DelegationWriteTestCase("putAllAdditionalQueryParams", QUERY_PARAMS), + DelegationWriteTestCase("putAllAdditionalQueryParams", MAP), + DelegationWriteTestCase("replaceAdditionalQueryParams", STRING, STRING), + DelegationWriteTestCase("replaceAdditionalQueryParams", STRING, LIST), + DelegationWriteTestCase("replaceAllAdditionalQueryParams", QUERY_PARAMS), + DelegationWriteTestCase("replaceAllAdditionalQueryParams", MAP), + DelegationWriteTestCase("removeAdditionalQueryParams", STRING), + DelegationWriteTestCase("removeAllAdditionalQueryParams", SET), + ) + } + + // New instances of the `mockBuilderDelegate` and `builderDelegator` are required for each test + // case (each test case runs in its own instance of the test class). + private val mockBuilderDelegate: ResponseCreateParams.Builder = + mock(ResponseCreateParams.Builder::class.java) + private val builderDelegator = + StructuredResponseCreateParams.builder().inject(mockBuilderDelegate) + + @Test + fun allBuilderDelegateFunctionsExistInDelegator() { + // The delegator class does not implement the various `text` functions or the `body` + // function of the delegate class. + checkAllDelegation(mockBuilderDelegate::class, builderDelegator::class, "body", "text") + } + + @Test + fun allBuilderDelegatorFunctionsExistInDelegate() { + // The delegator implements a different `text` function from those overloads in the delegate + // class. + checkAllDelegation(builderDelegator::class, mockBuilderDelegate::class, "text") + } + + @Test + fun allBuilderDelegatorFunctionsAreTested() { + checkAllDelegatorWriteFunctionsAreTested( + builderDelegator::class, + builderDelegationTestCases(), + exceptionalTestedFns = setOf("text"), + nonDelegatingFns = setOf("build", "wrap", "inject"), + ) + } + + @ParameterizedTest + @MethodSource("builderDelegationTestCases") + fun `delegation of Builder write functions`(testCase: DelegationWriteTestCase) { + checkOneDelegationWrite(builderDelegator, mockBuilderDelegate, testCase) + } + + @Test + fun `delegation of text`() { + // Special unit test case as the delegator method signature does not match that of the + // delegate method. + val delegatorTestCase = DelegationWriteTestCase("text", X::class.java) + val delegatorMethod = findDelegationMethod(builderDelegator, delegatorTestCase) + val mockDelegateTestCase = + DelegationWriteTestCase("text", textConfigFromClass(X::class.java)) + val mockDelegateMethod = findDelegationMethod(mockBuilderDelegate, mockDelegateTestCase) + + delegatorMethod.invoke(builderDelegator, delegatorTestCase.inputValues[0]) + + // Verify that the corresponding method on the mock delegate was called exactly once. + verify(mockBuilderDelegate, times(1)).apply { + mockDelegateMethod.invoke(mockBuilderDelegate, mockDelegateTestCase.inputValues[0]) + } + verifyNoMoreInteractions(mockBuilderDelegate) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseOutputItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseOutputItemTest.kt new file mode 100644 index 00000000..2ba6fa03 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseOutputItemTest.kt @@ -0,0 +1,204 @@ +package com.openai.models.responses + +import com.openai.core.DelegationReadTestCase +import com.openai.core.LIST +import com.openai.core.OPTIONAL +import com.openai.core.STRING +import com.openai.core.X +import com.openai.core.checkAllDelegation +import com.openai.core.checkAllDelegatorReadFunctionsAreTested +import com.openai.core.checkOneDelegationRead +import java.util.Optional +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import org.mockito.Mockito.mock +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.Mockito.`when` +import org.mockito.kotlin.times +import org.mockito.kotlin.verify + +/** + * Unit tests for the [StructuredResponseOutputItem] class (delegator) and its delegation of most + * functions to a wrapped [ResponseOutputItem] (delegate). The tests include confirmation of the + * following: + * - All functions in the delegator correspond to a function in the delegate and _vice versa_. + * - All functions in the delegator call their corresponding function in the delegate and only that + * function. + * - A unit test exists for all functions. + * + * There are some exceptions to the above that are handled differently. + */ +internal class StructuredResponseOutputItemTest { + companion object { + private val FILE_SEARCH_TOOL_CALL = + ResponseFileSearchToolCall.builder() + .id(STRING) + .queries(LIST) + .status(ResponseFileSearchToolCall.Status.COMPLETED) + .build() + private val FUNCTION_TOOL_CALL = + ResponseFunctionToolCall.builder().arguments(STRING).callId(STRING).name(STRING).build() + private val FUNCTION_WEB_SEARCH = + ResponseFunctionWebSearch.builder() + .id(STRING) + .status(ResponseFunctionWebSearch.Status.COMPLETED) + .build() + private val COMPUTER_TOOL_CALL = + ResponseComputerToolCall.builder() + .id(STRING) + .action(ResponseComputerToolCall.Action.ofWait()) + .callId(STRING) + .pendingSafetyChecks(listOf()) + .status(ResponseComputerToolCall.Status.COMPLETED) + .type(ResponseComputerToolCall.Type.COMPUTER_CALL) + .build() + private val REASONING_ITEM = + ResponseReasoningItem.builder().id(STRING).summary(listOf()).build() + private val MESSAGE = + ResponseOutputMessage.builder() + .id(STRING) + .content(listOf()) + .status(ResponseOutputMessage.Status.COMPLETED) + .build() + + // The list order follows the declaration order in `ResponseOutputItem` for easier + // maintenance. + @JvmStatic + private fun delegationTestCases() = + listOf( + // `message()` is a special case and has its own test function. + DelegationReadTestCase("fileSearchCall", OPTIONAL), + DelegationReadTestCase("functionCall", OPTIONAL), + DelegationReadTestCase("webSearchCall", OPTIONAL), + DelegationReadTestCase("computerCall", OPTIONAL), + DelegationReadTestCase("reasoning", OPTIONAL), + // `isMessage()` is a special case and has its own test function. + // For the Boolean functions, call each in turn with both `true` and `false` to + // ensure that a return value is not hard-coded. + DelegationReadTestCase("isFileSearchCall", true), + DelegationReadTestCase("isFileSearchCall", false), + DelegationReadTestCase("isFunctionCall", true), + DelegationReadTestCase("isFunctionCall", false), + DelegationReadTestCase("isWebSearchCall", true), + DelegationReadTestCase("isWebSearchCall", false), + DelegationReadTestCase("isComputerCall", true), + DelegationReadTestCase("isComputerCall", false), + DelegationReadTestCase("isReasoning", true), + DelegationReadTestCase("isReasoning", false), + // `asMessage()` is a special case and has its own test function. + DelegationReadTestCase("asFileSearchCall", FILE_SEARCH_TOOL_CALL), + DelegationReadTestCase("asFunctionCall", FUNCTION_TOOL_CALL), + DelegationReadTestCase("asWebSearchCall", FUNCTION_WEB_SEARCH), + DelegationReadTestCase("asComputerCall", COMPUTER_TOOL_CALL), + DelegationReadTestCase("asReasoning", REASONING_ITEM), + DelegationReadTestCase("_json", OPTIONAL), + ) + } + + // New instances of the `mockDelegate` and `delegator` are required for each test case (each + // test case runs in its own instance of the test class). + private val mockDelegate: ResponseOutputItem = mock(ResponseOutputItem::class.java) + private val delegator = StructuredResponseOutputItem(X::class.java, mockDelegate) + + @Test + fun allDelegateFunctionsExistInDelegator() { + // `toBuilder()` is deliberately not implemented. `accept()` has a different signature. + checkAllDelegation(mockDelegate::class, delegator::class, "toBuilder", "accept") + } + + @Test + fun allDelegatorFunctionsExistInDelegate() { + // `accept()` has a different signature. + checkAllDelegation(delegator::class, mockDelegate::class, "accept") + } + + @Test + fun allDelegatorFunctionsAreTested() { + // There are exceptional test cases for some functions. Most other functions are part of the + // list of those using the parameterized test. A few delegator functions do not delegate, so + // no test function is necessary. + checkAllDelegatorReadFunctionsAreTested( + delegator::class, + delegationTestCases(), + exceptionalTestedFns = + setOf("message", "asMessage", "isMessage", "validate", "isValid", "accept"), + nonDelegatingFns = setOf("equals", "hashCode", "toString"), + ) + } + + @ParameterizedTest + @MethodSource("delegationTestCases") + fun `delegation of functions in general`(testCase: DelegationReadTestCase) { + checkOneDelegationRead(delegator, mockDelegate, testCase) + } + + @Test + fun `delegation of message`() { + // Input and output are different types, so this test is an exceptional case. + // The delegator's `message()` delegates to the delegate's `message()` indirectly via the + // delegator's `message` field initializer. + val input = Optional.of(MESSAGE) + `when`(mockDelegate.message()).thenReturn(input) + val output = delegator.message() + + verify(mockDelegate, times(1)).message() + verifyNoMoreInteractions(mockDelegate) + + assertThat(output.get().rawMessage).isEqualTo(MESSAGE) + } + + @Test + fun `delegation of asMessage`() { + // Delegation function names do not match, so this test is an exceptional case. + // The delegator's `asMessage()` delegates to the delegate's `message()` (without the "as") + // indirectly via the delegator's `message` field initializer. + val input = Optional.of(MESSAGE) + `when`(mockDelegate.message()).thenReturn(input) + val output = delegator.asMessage() + + verify(mockDelegate, times(1)).message() + verifyNoMoreInteractions(mockDelegate) + + assertThat(output.rawMessage).isEqualTo(MESSAGE) + } + + @Test + fun `delegation of isMessage`() { + // Delegation function names do not match, so this test is an exceptional case. + // The delegator's `isMessage()` delegates to the delegate's `message()` (without the "is") + // indirectly via the delegator's `message` field initializer. + val input = Optional.of(MESSAGE) + `when`(mockDelegate.message()).thenReturn(input) + val output = delegator.isMessage() + + verify(mockDelegate, times(1)).message() + verifyNoMoreInteractions(mockDelegate) + + assertThat(output).isTrue + } + + @Test + fun `delegation of validate`() { + `when`(mockDelegate.message()).thenReturn(Optional.of(MESSAGE)) + + delegator.validate() + + // Delegator's `validate()` does not call delegate's `validate()`. `message()` is called + // indirectly via the `message` field initializer. + verify(mockDelegate, times(1)).message() + verifyNoMoreInteractions(mockDelegate) + } + + @Test + fun `delegation of isValid`() { + // `isValid` calls `validate()`, so the test is similar to that for `validate()`. + `when`(mockDelegate.message()).thenReturn(Optional.of(MESSAGE)) + + delegator.isValid() + + verify(mockDelegate, times(1)).message() + verifyNoMoreInteractions(mockDelegate) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseOutputMessageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseOutputMessageTest.kt new file mode 100644 index 00000000..2b093cf5 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseOutputMessageTest.kt @@ -0,0 +1,285 @@ +package com.openai.models.responses + +import com.openai.core.DelegationReadTestCase +import com.openai.core.JSON_FIELD +import com.openai.core.JSON_VALUE +import com.openai.core.JsonField +import com.openai.core.MAP +import com.openai.core.OPTIONAL +import com.openai.core.STRING +import com.openai.core.X +import com.openai.core.checkAllDelegation +import com.openai.core.checkAllDelegatorReadFunctionsAreTested +import com.openai.core.checkOneDelegationRead +import com.openai.errors.OpenAIInvalidDataException +import java.util.Optional +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import org.mockito.Mockito.mock +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.Mockito.`when` +import org.mockito.kotlin.times +import org.mockito.kotlin.verify + +/** + * Unit tests for the [StructuredResponseOutputMessage] class (delegator) and its delegation of most + * functions to a wrapped [ResponseOutputMessage] (delegate). The tests include confirmation of the + * following: + * - All functions in the delegator correspond to a function in the delegate and _vice versa_. + * - All functions in the delegator call their corresponding function in the delegate and only that + * function. + * - A unit test exists for all functions. + * + * There are some exceptions to the above that are handled differently. + */ +internal class StructuredResponseOutputMessageTest { + companion object { + private val MESSAGE_STATUS = ResponseOutputMessage.Status.COMPLETED + private val OUTPUT_TEXT = + ResponseOutputText.builder().annotations(listOf()).text(STRING).build() + private val OUTPUT_REFUSAL = ResponseOutputRefusal.builder().refusal(STRING).build() + private val CONTENT = ResponseOutputMessage.Content.ofOutputText(OUTPUT_TEXT) + + // The list order follows the declaration order in `ResponseOutputMessage` for easier + // maintenance. + @JvmStatic + private fun delegationTestCases() = + listOf( + DelegationReadTestCase("id", STRING), + // `content()` is a special case and has its own test function. + DelegationReadTestCase("_role", JSON_VALUE), + DelegationReadTestCase("status", MESSAGE_STATUS), + DelegationReadTestCase("_type", JSON_VALUE), + DelegationReadTestCase("_id", JSON_FIELD), + // `_content()` is a special case and has its own test function. + DelegationReadTestCase("_status", JSON_FIELD), + DelegationReadTestCase("_additionalProperties", MAP), + ) + + // The list order follows the declaration order in `ResponseOutputMessage.Content` for + // easier maintenance. + @JvmStatic + private fun contentDelegationTestCases() = + listOf( + // `outputText()` is a special case and has its own test function. + DelegationReadTestCase("refusal", OPTIONAL), + // For the Boolean functions, pass both `true` and `false` to ensure that one value + // is not hard-coded. + DelegationReadTestCase("isOutputText", true), + DelegationReadTestCase("isOutputText", false), + DelegationReadTestCase("isRefusal", true), + DelegationReadTestCase("isRefusal", false), + // `asOutputText()` is a special case and has its own test function. + DelegationReadTestCase("asRefusal", OUTPUT_REFUSAL), + DelegationReadTestCase("_json", OPTIONAL), + ) + } + + // New instances of the `mockDelegate` and `delegator` are required for each test case (each + // test case runs in its own instance of the test class). + private val mockDelegate: ResponseOutputMessage = mock(ResponseOutputMessage::class.java) + private val delegator = StructuredResponseOutputMessage(X::class.java, mockDelegate) + + private val contentMockDelegate: ResponseOutputMessage.Content = + mock(ResponseOutputMessage.Content::class.java) + private val contentDelegator = + StructuredResponseOutputMessage.Content(X::class.java, contentMockDelegate) + + @Test + fun allDelegateFunctionsExistInDelegator() { + checkAllDelegation(mockDelegate::class, delegator::class, "toBuilder") + } + + @Test + fun allDelegatorFunctionsExistInDelegate() { + checkAllDelegation(delegator::class, mockDelegate::class) + } + + @Test + fun allContentDelegateFunctionsExistInDelegator() { + // The `Content.accept()` function in the delegator takes a different type than that in the + // delegate, so there is no delegation from the former to the latter. `Content.toBuilder` is + // deliberately not implemented. + checkAllDelegation( + contentMockDelegate::class, + contentDelegator::class, + "toBuilder", + "accept", + ) + } + + @Test + fun allContentDelegatorFunctionsExistInDelegate() { + // The `Content.accept()` function in the delegator takes a different type than that in the + // delegate, so there is no delegation from the former to the latter. + checkAllDelegation(contentDelegator::class, contentMockDelegate::class, "accept") + } + + @Test + fun allDelegatorFunctionsAreTested() { + // There are exceptional test cases for some functions. Most other functions are part of the + // list of those using the parameterized test. A few delegator functions do not delegate, so + // no test function is necessary. + checkAllDelegatorReadFunctionsAreTested( + delegator::class, + delegationTestCases(), + exceptionalTestedFns = setOf("content", "_content", "validate", "isValid"), + nonDelegatingFns = setOf("equals", "hashCode", "toString"), + ) + } + + @Test + fun allContentDelegatorFunctionsAreTested() { + checkAllDelegatorReadFunctionsAreTested( + contentDelegator::class, + contentDelegationTestCases(), + exceptionalTestedFns = + setOf("outputText", "asOutputText", "validate", "isValid", "accept"), + nonDelegatingFns = setOf("equals", "hashCode", "toString"), + ) + } + + @ParameterizedTest + @MethodSource("delegationTestCases") + fun `delegation of functions in general`(testCase: DelegationReadTestCase) { + checkOneDelegationRead(delegator, mockDelegate, testCase) + } + + @ParameterizedTest + @MethodSource("contentDelegationTestCases") + fun `delegation of Content functions in general`(testCase: DelegationReadTestCase) { + checkOneDelegationRead(contentDelegator, contentMockDelegate, testCase) + } + + @Test + fun `delegation of content`() { + // Input and output are different types, so this test is an exceptional case. + // `content()` (without an underscore) delegates to `_content()` (with an underscore) + // indirectly via the `content` field initializer. + val input = JsonField.of(listOf(CONTENT)) + `when`(mockDelegate._content()).thenReturn(input) + val output = delegator.content() // Without an underscore. + + verify(mockDelegate, times(1))._content() + verifyNoMoreInteractions(mockDelegate) + + assertThat(output[0].rawContent).isEqualTo(CONTENT) + } + + @Test + fun `delegation of _content`() { + // Input and output are different types, so this test is an exceptional case. + // `_content()` (with an underscore) delegates to `_content()` (with an underscore) + // indirectly via the `content` field initializer. + val input = JsonField.of(listOf(CONTENT)) + `when`(mockDelegate._content()).thenReturn(input) + val output = delegator._content() // With an underscore. + + verify(mockDelegate, times(1))._content() + verifyNoMoreInteractions(mockDelegate) + + assertThat(output.getRequired("content")[0].rawContent).isEqualTo(CONTENT) + } + + @Test + fun `delegation of validate`() { + `when`(mockDelegate._content()).thenReturn(JsonField.of(listOf(CONTENT))) + + delegator.validate() + + // Delegator's `validate()` calls delegate's `validate()`. `_content` is called indirectly + // via the `content` field initializer. + verify(mockDelegate, times(1))._content() + verify(mockDelegate, times(1)).validate() + verifyNoMoreInteractions(mockDelegate) + } + + @Test + fun `delegation of isValid`() { + // `isValid` calls `validate()`, so the test is similar to that for `validate()`. + `when`(mockDelegate._content()).thenReturn(JsonField.of(listOf(CONTENT))) + + delegator.isValid() + + verify(mockDelegate, times(1))._content() + verify(mockDelegate, times(1)).validate() + verifyNoMoreInteractions(mockDelegate) + } + + @Test + fun `delegation of Content outputText`() { + // Input and output are different types, so this test is an exceptional case. The + // delegator's `outputText()` delegates to the delegate's `outputText()` indirectly via the + // `outputText` field initializer. + val input = + Optional.of( + ResponseOutputText.builder() + .annotations(listOf()) + .text("{\"s\" : \"hello\"}") + .build() + ) + `when`(contentMockDelegate.outputText()).thenReturn(input) + val output = contentDelegator.outputText() + + verify(contentMockDelegate, times(1)).outputText() + verifyNoMoreInteractions(contentMockDelegate) + + assertThat(output).isEqualTo(Optional.of(X("hello"))) + } + + @Test + fun `delegation of Content asOutputText`() { + // Input and output are different types, so this test is an exceptional case. The + // delegator's `asOutputText()` delegates to the delegate's `outputText()` indirectly via + // the + // `outputText` field initializer. + val input = + Optional.of( + ResponseOutputText.builder() + .annotations(listOf()) + .text("{\"s\" : \"hello\"}") + .build() + ) + `when`(contentMockDelegate.outputText()).thenReturn(input) + val output = contentDelegator.asOutputText() + + verify(contentMockDelegate, times(1)).outputText() + verifyNoMoreInteractions(contentMockDelegate) + + assertThat(output).isEqualTo(X("hello")) + } + + @Test + fun `delegation of Content asOutputText missing`() { + val input = Optional.ofNullable(null) + `when`(contentMockDelegate.outputText()).thenReturn(input) + + assertThatThrownBy { contentDelegator.asOutputText() } + .isInstanceOf(OpenAIInvalidDataException::class.java) + .hasMessage("`outputText` is not present") + + verify(contentMockDelegate, times(1)).outputText() + verifyNoMoreInteractions(contentMockDelegate) + } + + @Test + fun `delegation of Content validate`() { + // No values or passed and only `this` is returned. + contentDelegator.validate() + + verify(contentMockDelegate, times(1)).validate() + verifyNoMoreInteractions(contentMockDelegate) + } + + @Test + fun `delegation of Content isValid`() { + contentDelegator.isValid() + + // `isValid()` calls `validate`, which then calls the mock delegate. + verify(contentMockDelegate, times(1)).validate() + verifyNoMoreInteractions(contentMockDelegate) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseTest.kt new file mode 100644 index 00000000..9a1b76cd --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseTest.kt @@ -0,0 +1,197 @@ +package com.openai.models.responses + +import com.openai.core.BOOLEAN +import com.openai.core.DOUBLE +import com.openai.core.DelegationReadTestCase +import com.openai.core.JSON_FIELD +import com.openai.core.JSON_VALUE +import com.openai.core.JsonField +import com.openai.core.LIST +import com.openai.core.MAP +import com.openai.core.OPTIONAL +import com.openai.core.STRING +import com.openai.core.X +import com.openai.core.checkAllDelegation +import com.openai.core.checkAllDelegatorReadFunctionsAreTested +import com.openai.core.checkOneDelegationRead +import com.openai.models.ChatModel +import com.openai.models.ResponsesModel +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import org.mockito.Mockito.mock +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.Mockito.`when` +import org.mockito.kotlin.times +import org.mockito.kotlin.verify + +/** + * Unit tests for the [StructuredResponse] class (delegator) and its delegation of most functions to + * a wrapped [Response] (delegate). The tests include confirmation of the following: + * - All functions in the delegator correspond to a function in the delegate and _vice versa_. + * - All functions in the delegator call their corresponding function in the delegate and only that + * function. + * - A unit test exists for all functions. + * + * There are some exceptions to the above that are handled differently. + */ +internal class StructuredResponseTest { + companion object { + private val RESPONSES_MODEL = ResponsesModel.ofChat(ChatModel.GPT_4O) + + private val TOOL_CHOICE = + Response.ToolChoice.ofFunction(ToolChoiceFunction.builder().name(STRING).build()) + // A reasoning item is probably the simplest one to create. + private val OUTPUT_ITEM = + ResponseOutputItem.ofReasoning( + ResponseReasoningItem.builder() + .id(STRING) + .summary(listOf(ResponseReasoningItem.Summary.builder().text(STRING).build())) + .build() + ) + + // The list order follows the declaration order in `Response` for easier maintenance. + @JvmStatic + private fun delegationTestCases() = + listOf( + DelegationReadTestCase("id", STRING), + DelegationReadTestCase("createdAt", DOUBLE), + DelegationReadTestCase("error", OPTIONAL), + DelegationReadTestCase("incompleteDetails", OPTIONAL), + DelegationReadTestCase("instructions", OPTIONAL), + DelegationReadTestCase("metadata", OPTIONAL), + DelegationReadTestCase("model", RESPONSES_MODEL), + DelegationReadTestCase("_object_", JSON_VALUE), + // `output()` is a special case and has its own test function. + DelegationReadTestCase("parallelToolCalls", BOOLEAN), + DelegationReadTestCase("temperature", OPTIONAL), + DelegationReadTestCase("toolChoice", TOOL_CHOICE), + DelegationReadTestCase("tools", LIST), + DelegationReadTestCase("topP", OPTIONAL), + DelegationReadTestCase("maxOutputTokens", OPTIONAL), + DelegationReadTestCase("previousResponseId", OPTIONAL), + DelegationReadTestCase("reasoning", OPTIONAL), + DelegationReadTestCase("serviceTier", OPTIONAL), + DelegationReadTestCase("status", OPTIONAL), + DelegationReadTestCase("text", OPTIONAL), + DelegationReadTestCase("truncation", OPTIONAL), + DelegationReadTestCase("usage", OPTIONAL), + DelegationReadTestCase("user", OPTIONAL), + DelegationReadTestCase("_id", JSON_FIELD), + DelegationReadTestCase("_createdAt", JSON_FIELD), + DelegationReadTestCase("_error", JSON_FIELD), + DelegationReadTestCase("_incompleteDetails", JSON_FIELD), + DelegationReadTestCase("_instructions", JSON_FIELD), + DelegationReadTestCase("_metadata", JSON_FIELD), + DelegationReadTestCase("_model", JSON_FIELD), + // `_output()` is a special case and has its own test function. + DelegationReadTestCase("_parallelToolCalls", JSON_FIELD), + DelegationReadTestCase("_temperature", JSON_FIELD), + DelegationReadTestCase("_toolChoice", JSON_FIELD), + DelegationReadTestCase("_tools", JSON_FIELD), + DelegationReadTestCase("_topP", JSON_FIELD), + DelegationReadTestCase("_maxOutputTokens", JSON_FIELD), + DelegationReadTestCase("_previousResponseId", JSON_FIELD), + DelegationReadTestCase("_reasoning", JSON_FIELD), + DelegationReadTestCase("_serviceTier", JSON_FIELD), + DelegationReadTestCase("_status", JSON_FIELD), + DelegationReadTestCase("_text", JSON_FIELD), + DelegationReadTestCase("_truncation", JSON_FIELD), + DelegationReadTestCase("_usage", JSON_FIELD), + DelegationReadTestCase("_user", JSON_FIELD), + DelegationReadTestCase("_additionalProperties", MAP), + // `validate()` and `isValid()` (which calls `validate()`) are tested separately, + // as they require special handling. + ) + } + + // New instances of the `mockDelegate` and `delegator` are required for each test case (each + // test case runs in its own instance of the test class). + private val mockDelegate: Response = mock(Response::class.java) + private val delegator = StructuredResponse(X::class.java, mockDelegate) + + @Test + fun allDelegateFunctionsExistInDelegator() { + checkAllDelegation(mockDelegate::class, delegator::class, "toBuilder") + } + + @Test + fun allDelegatorFunctionsExistInDelegate() { + checkAllDelegation(delegator::class, mockDelegate::class) + } + + @Test + fun allDelegatorFunctionsAreTested() { + // There are exceptional test cases for some functions. Most other functions are part of the + // list of those using the parameterized test. A few delegator functions do not delegate, so + // no test function is necessary. + checkAllDelegatorReadFunctionsAreTested( + delegator::class, + delegationTestCases(), + exceptionalTestedFns = setOf("output", "_output", "validate", "isValid"), + nonDelegatingFns = setOf("equals", "hashCode", "toString"), + ) + } + + @ParameterizedTest + @MethodSource("delegationTestCases") + fun `delegation of functions in general`(testCase: DelegationReadTestCase) { + checkOneDelegationRead(delegator, mockDelegate, testCase) + } + + @Test + fun `delegation of output`() { + // Input and output are different types, so this test is an exceptional case. + // `output()` (without an underscore) delegates to `_output()` (with an underscore) + // indirectly via the `output` field initializer. + val input = JsonField.of(listOf(OUTPUT_ITEM)) + `when`(mockDelegate._output()).thenReturn(input) + val output = delegator.output() // Without an underscore. + + verify(mockDelegate, times(1))._output() + verifyNoMoreInteractions(mockDelegate) + + assertThat(output[0].rawOutputItem).isEqualTo(OUTPUT_ITEM) + } + + @Test + fun `delegation of _output`() { + // Input and output are different types, so this test is an exceptional case. + // `_output()` delegates to `_output()` indirectly via the `output` field initializer. + val input = JsonField.of(listOf(OUTPUT_ITEM)) + `when`(mockDelegate._output()).thenReturn(input) + val output = delegator._output() // With an underscore. + + verify(mockDelegate, times(1))._output() + verifyNoMoreInteractions(mockDelegate) + + assertThat(output.getRequired("_output")[0].rawOutputItem).isEqualTo(OUTPUT_ITEM) + } + + @Test + fun `delegation of validate`() { + `when`(mockDelegate._output()).thenReturn(JsonField.of(listOf(OUTPUT_ITEM))) + + delegator.validate() + + // Delegator's `validate()` calls the delegate's `validate()`. Delegate's `_output()` is + // called indirectly via the `output` field initializer. + verify(mockDelegate, times(1))._output() // Indirect + verify(mockDelegate, times(1)).validate() + verifyNoMoreInteractions(mockDelegate) + } + + @Test + fun `delegation of isValid`() { + // `isValid()` calls `validate()` which delegates to `validate()`, so the test is + // more-or-less the same as for `validate()`. + `when`(mockDelegate._output()).thenReturn(JsonField.of(listOf(OUTPUT_ITEM))) + + delegator.isValid() + + verify(mockDelegate, times(1))._output() // Indirect + verify(mockDelegate, times(1)).validate() + verifyNoMoreInteractions(mockDelegate) + } +} 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 f7e594be..f19d5a98 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 @@ -23,6 +23,13 @@ import com.openai.errors.UnauthorizedException import com.openai.errors.UnexpectedStatusCodeException import com.openai.errors.UnprocessableEntityException import com.openai.models.finetuning.jobs.JobCreateParams +import com.openai.models.finetuning.methods.DpoHyperparameters +import com.openai.models.finetuning.methods.DpoMethod +import com.openai.models.finetuning.methods.ReinforcementHyperparameters +import com.openai.models.finetuning.methods.ReinforcementMethod +import com.openai.models.finetuning.methods.SupervisedHyperparameters +import com.openai.models.finetuning.methods.SupervisedMethod +import com.openai.models.graders.gradermodels.StringCheckGrader import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.entry import org.junit.jupiter.api.BeforeEach @@ -111,10 +118,11 @@ internal class ErrorHandlingTest { ) .method( JobCreateParams.Method.builder() + .type(JobCreateParams.Method.Type.SUPERVISED) .dpo( - JobCreateParams.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - JobCreateParams.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -123,11 +131,36 @@ internal class ErrorHandlingTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort + .DEFAULT + ) + .build() + ) + .build() + ) .supervised( - JobCreateParams.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - JobCreateParams.Method.Supervised.Hyperparameters - .builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -135,7 +168,6 @@ internal class ErrorHandlingTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) .seed(42L) @@ -202,10 +234,11 @@ internal class ErrorHandlingTest { ) .method( JobCreateParams.Method.builder() + .type(JobCreateParams.Method.Type.SUPERVISED) .dpo( - JobCreateParams.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - JobCreateParams.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -214,11 +247,36 @@ internal class ErrorHandlingTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort + .DEFAULT + ) + .build() + ) + .build() + ) .supervised( - JobCreateParams.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - JobCreateParams.Method.Supervised.Hyperparameters - .builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -226,7 +284,6 @@ internal class ErrorHandlingTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) .seed(42L) @@ -293,10 +350,11 @@ internal class ErrorHandlingTest { ) .method( JobCreateParams.Method.builder() + .type(JobCreateParams.Method.Type.SUPERVISED) .dpo( - JobCreateParams.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - JobCreateParams.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -305,11 +363,36 @@ internal class ErrorHandlingTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort + .DEFAULT + ) + .build() + ) + .build() + ) .supervised( - JobCreateParams.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - JobCreateParams.Method.Supervised.Hyperparameters - .builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -317,7 +400,6 @@ internal class ErrorHandlingTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) .seed(42L) @@ -384,10 +466,11 @@ internal class ErrorHandlingTest { ) .method( JobCreateParams.Method.builder() + .type(JobCreateParams.Method.Type.SUPERVISED) .dpo( - JobCreateParams.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - JobCreateParams.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -396,11 +479,36 @@ internal class ErrorHandlingTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort + .DEFAULT + ) + .build() + ) + .build() + ) .supervised( - JobCreateParams.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - JobCreateParams.Method.Supervised.Hyperparameters - .builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -408,7 +516,6 @@ internal class ErrorHandlingTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) .seed(42L) @@ -475,10 +582,11 @@ internal class ErrorHandlingTest { ) .method( JobCreateParams.Method.builder() + .type(JobCreateParams.Method.Type.SUPERVISED) .dpo( - JobCreateParams.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - JobCreateParams.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -487,11 +595,36 @@ internal class ErrorHandlingTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort + .DEFAULT + ) + .build() + ) + .build() + ) .supervised( - JobCreateParams.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - JobCreateParams.Method.Supervised.Hyperparameters - .builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -499,7 +632,6 @@ internal class ErrorHandlingTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) .seed(42L) @@ -566,10 +698,11 @@ internal class ErrorHandlingTest { ) .method( JobCreateParams.Method.builder() + .type(JobCreateParams.Method.Type.SUPERVISED) .dpo( - JobCreateParams.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - JobCreateParams.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -578,11 +711,36 @@ internal class ErrorHandlingTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort + .DEFAULT + ) + .build() + ) + .build() + ) .supervised( - JobCreateParams.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - JobCreateParams.Method.Supervised.Hyperparameters - .builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -590,7 +748,6 @@ internal class ErrorHandlingTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) .seed(42L) @@ -657,10 +814,11 @@ internal class ErrorHandlingTest { ) .method( JobCreateParams.Method.builder() + .type(JobCreateParams.Method.Type.SUPERVISED) .dpo( - JobCreateParams.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - JobCreateParams.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -669,11 +827,36 @@ internal class ErrorHandlingTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort + .DEFAULT + ) + .build() + ) + .build() + ) .supervised( - JobCreateParams.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - JobCreateParams.Method.Supervised.Hyperparameters - .builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -681,7 +864,6 @@ internal class ErrorHandlingTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) .seed(42L) @@ -748,10 +930,11 @@ internal class ErrorHandlingTest { ) .method( JobCreateParams.Method.builder() + .type(JobCreateParams.Method.Type.SUPERVISED) .dpo( - JobCreateParams.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - JobCreateParams.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -760,11 +943,36 @@ internal class ErrorHandlingTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort + .DEFAULT + ) + .build() + ) + .build() + ) .supervised( - JobCreateParams.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - JobCreateParams.Method.Supervised.Hyperparameters - .builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -772,7 +980,6 @@ internal class ErrorHandlingTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) .seed(42L) @@ -837,10 +1044,11 @@ internal class ErrorHandlingTest { ) .method( JobCreateParams.Method.builder() + .type(JobCreateParams.Method.Type.SUPERVISED) .dpo( - JobCreateParams.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - JobCreateParams.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -849,11 +1057,36 @@ internal class ErrorHandlingTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort + .DEFAULT + ) + .build() + ) + .build() + ) .supervised( - JobCreateParams.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - JobCreateParams.Method.Supervised.Hyperparameters - .builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -861,7 +1094,6 @@ internal class ErrorHandlingTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) .seed(42L) diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/BatchServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/BatchServiceAsyncTest.kt index 25228c55..bddf750d 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/BatchServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/BatchServiceAsyncTest.kt @@ -5,9 +5,7 @@ package com.openai.services.async import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClientAsync import com.openai.core.JsonValue -import com.openai.models.batches.BatchCancelParams import com.openai.models.batches.BatchCreateParams -import com.openai.models.batches.BatchRetrieveParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -50,8 +48,7 @@ internal class BatchServiceAsyncTest { .build() val batchServiceAsync = client.batches() - val batchFuture = - batchServiceAsync.retrieve(BatchRetrieveParams.builder().batchId("batch_id").build()) + val batchFuture = batchServiceAsync.retrieve("batch_id") val batch = batchFuture.get() batch.validate() @@ -81,8 +78,7 @@ internal class BatchServiceAsyncTest { .build() val batchServiceAsync = client.batches() - val batchFuture = - batchServiceAsync.cancel(BatchCancelParams.builder().batchId("batch_id").build()) + val batchFuture = batchServiceAsync.cancel("batch_id") val batch = batchFuture.get() batch.validate() diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/EvalServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/EvalServiceAsyncTest.kt index b4a7c4fb..1f940b24 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/EvalServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/EvalServiceAsyncTest.kt @@ -6,8 +6,6 @@ import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClientAsync import com.openai.core.JsonValue import com.openai.models.evals.EvalCreateParams -import com.openai.models.evals.EvalDeleteParams -import com.openai.models.evals.EvalRetrieveParams import com.openai.models.evals.EvalUpdateParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -75,8 +73,7 @@ internal class EvalServiceAsyncTest { .build() val evalServiceAsync = client.evals() - val evalFuture = - evalServiceAsync.retrieve(EvalRetrieveParams.builder().evalId("eval_id").build()) + val evalFuture = evalServiceAsync.retrieve("eval_id") val eval = evalFuture.get() eval.validate() @@ -132,8 +129,7 @@ internal class EvalServiceAsyncTest { .build() val evalServiceAsync = client.evals() - val evalFuture = - evalServiceAsync.delete(EvalDeleteParams.builder().evalId("eval_id").build()) + val evalFuture = evalServiceAsync.delete("eval_id") val eval = evalFuture.get() eval.validate() 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 6c8af86c..0156759e 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 @@ -10,11 +10,8 @@ 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.files.FileContentParams import com.openai.models.files.FileCreateParams -import com.openai.models.files.FileDeleteParams import com.openai.models.files.FilePurpose -import com.openai.models.files.FileRetrieveParams import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -55,8 +52,7 @@ internal class FileServiceAsyncTest { .build() val fileServiceAsync = client.files() - val fileObjectFuture = - fileServiceAsync.retrieve(FileRetrieveParams.builder().fileId("file_id").build()) + val fileObjectFuture = fileServiceAsync.retrieve("file_id") val fileObject = fileObjectFuture.get() fileObject.validate() @@ -86,8 +82,7 @@ internal class FileServiceAsyncTest { .build() val fileServiceAsync = client.files() - val fileDeletedFuture = - fileServiceAsync.delete(FileDeleteParams.builder().fileId("file_id").build()) + val fileDeletedFuture = fileServiceAsync.delete("file_id") val fileDeleted = fileDeletedFuture.get() fileDeleted.validate() @@ -103,8 +98,7 @@ internal class FileServiceAsyncTest { val fileServiceAsync = client.files() stubFor(get(anyUrl()).willReturn(ok().withBody("abc"))) - val responseFuture = - fileServiceAsync.content(FileContentParams.builder().fileId("file_id").build()) + val responseFuture = fileServiceAsync.content("file_id") val response = responseFuture.get() assertThat(response.body()).hasContent("abc") diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/ModelServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/ModelServiceAsyncTest.kt index 25b7ab23..95ab7c7f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/ModelServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/ModelServiceAsyncTest.kt @@ -4,8 +4,6 @@ package com.openai.services.async import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClientAsync -import com.openai.models.models.ModelDeleteParams -import com.openai.models.models.ModelRetrieveParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -21,8 +19,7 @@ internal class ModelServiceAsyncTest { .build() val modelServiceAsync = client.models() - val modelFuture = - modelServiceAsync.retrieve(ModelRetrieveParams.builder().model("gpt-4o-mini").build()) + val modelFuture = modelServiceAsync.retrieve("gpt-4o-mini") val model = modelFuture.get() model.validate() @@ -52,10 +49,7 @@ internal class ModelServiceAsyncTest { .build() val modelServiceAsync = client.models() - val modelDeletedFuture = - modelServiceAsync.delete( - ModelDeleteParams.builder().model("ft:gpt-4o-mini:acemeco:suffix:abc123").build() - ) + val modelDeletedFuture = modelServiceAsync.delete("ft:gpt-4o-mini:acemeco:suffix:abc123") val modelDeleted = modelDeletedFuture.get() modelDeleted.validate() diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/ResponseServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/ResponseServiceAsyncTest.kt index a7f6183b..8e6658e3 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/ResponseServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/ResponseServiceAsyncTest.kt @@ -12,7 +12,6 @@ import com.openai.models.ReasoningEffort import com.openai.models.ResponseFormatText import com.openai.models.responses.FileSearchTool import com.openai.models.responses.ResponseCreateParams -import com.openai.models.responses.ResponseDeleteParams import com.openai.models.responses.ResponseIncludable import com.openai.models.responses.ResponseRetrieveParams import com.openai.models.responses.ResponseTextConfig @@ -192,12 +191,7 @@ internal class ResponseServiceAsyncTest { .build() val responseServiceAsync = client.responses() - val future = - responseServiceAsync.delete( - ResponseDeleteParams.builder() - .responseId("resp_677efb5139a88190b512bc3fef8e535d") - .build() - ) + val future = responseServiceAsync.delete("resp_677efb5139a88190b512bc3fef8e535d") val response = future.get() } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/UploadServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/UploadServiceAsyncTest.kt index 0b652b22..48edbd35 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/UploadServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/UploadServiceAsyncTest.kt @@ -5,7 +5,6 @@ package com.openai.services.async import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClientAsync import com.openai.models.files.FilePurpose -import com.openai.models.uploads.UploadCancelParams import com.openai.models.uploads.UploadCompleteParams import com.openai.models.uploads.UploadCreateParams import org.junit.jupiter.api.Test @@ -46,10 +45,7 @@ internal class UploadServiceAsyncTest { .build() val uploadServiceAsync = client.uploads() - val uploadFuture = - uploadServiceAsync.cancel( - UploadCancelParams.builder().uploadId("upload_abc123").build() - ) + val uploadFuture = uploadServiceAsync.cancel("upload_abc123") val upload = uploadFuture.get() upload.validate() diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/VectorStoreServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/VectorStoreServiceAsyncTest.kt index 90a9bf88..63306b10 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/VectorStoreServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/VectorStoreServiceAsyncTest.kt @@ -7,8 +7,6 @@ import com.openai.client.okhttp.OpenAIOkHttpClientAsync import com.openai.core.JsonValue import com.openai.models.vectorstores.AutoFileChunkingStrategyParam import com.openai.models.vectorstores.VectorStoreCreateParams -import com.openai.models.vectorstores.VectorStoreDeleteParams -import com.openai.models.vectorstores.VectorStoreRetrieveParams import com.openai.models.vectorstores.VectorStoreSearchParams import com.openai.models.vectorstores.VectorStoreUpdateParams import org.junit.jupiter.api.Test @@ -54,10 +52,7 @@ internal class VectorStoreServiceAsyncTest { .build() val vectorStoreServiceAsync = client.vectorStores() - val vectorStoreFuture = - vectorStoreServiceAsync.retrieve( - VectorStoreRetrieveParams.builder().vectorStoreId("vector_store_id").build() - ) + val vectorStoreFuture = vectorStoreServiceAsync.retrieve("vector_store_id") val vectorStore = vectorStoreFuture.get() vectorStore.validate() @@ -114,10 +109,7 @@ internal class VectorStoreServiceAsyncTest { .build() val vectorStoreServiceAsync = client.vectorStores() - val vectorStoreDeletedFuture = - vectorStoreServiceAsync.delete( - VectorStoreDeleteParams.builder().vectorStoreId("vector_store_id").build() - ) + val vectorStoreDeletedFuture = vectorStoreServiceAsync.delete("vector_store_id") val vectorStoreDeleted = vectorStoreDeletedFuture.get() vectorStoreDeleted.validate() 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 index 4607a3b7..c80ab50a 100644 --- 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 @@ -28,6 +28,7 @@ internal class TranscriptionServiceAsyncTest { TranscriptionCreateParams.builder() .file("some content".byteInputStream()) .model(AudioModel.WHISPER_1) + .chunkingStrategyAuto() .addInclude(TranscriptionInclude.LOGPROBS) .language("language") .prompt("prompt") @@ -55,6 +56,7 @@ internal class TranscriptionServiceAsyncTest { TranscriptionCreateParams.builder() .file("some content".byteInputStream()) .model(AudioModel.WHISPER_1) + .chunkingStrategyAuto() .addInclude(TranscriptionInclude.LOGPROBS) .language("language") .prompt("prompt") diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/beta/AssistantServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/beta/AssistantServiceAsyncTest.kt index b731d493..bf5eebb4 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/beta/AssistantServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/beta/AssistantServiceAsyncTest.kt @@ -8,8 +8,6 @@ import com.openai.core.JsonValue import com.openai.models.ChatModel import com.openai.models.ReasoningEffort import com.openai.models.beta.assistants.AssistantCreateParams -import com.openai.models.beta.assistants.AssistantDeleteParams -import com.openai.models.beta.assistants.AssistantRetrieveParams import com.openai.models.beta.assistants.AssistantUpdateParams import com.openai.models.beta.assistants.CodeInterpreterTool import org.junit.jupiter.api.Test @@ -92,10 +90,7 @@ internal class AssistantServiceAsyncTest { .build() val assistantServiceAsync = client.beta().assistants() - val assistantFuture = - assistantServiceAsync.retrieve( - AssistantRetrieveParams.builder().assistantId("assistant_id").build() - ) + val assistantFuture = assistantServiceAsync.retrieve("assistant_id") val assistant = assistantFuture.get() assistant.validate() @@ -173,10 +168,7 @@ internal class AssistantServiceAsyncTest { .build() val assistantServiceAsync = client.beta().assistants() - val assistantDeletedFuture = - assistantServiceAsync.delete( - AssistantDeleteParams.builder().assistantId("assistant_id").build() - ) + val assistantDeletedFuture = assistantServiceAsync.delete("assistant_id") val assistantDeleted = assistantDeletedFuture.get() assistantDeleted.validate() diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/beta/ThreadServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/beta/ThreadServiceAsyncTest.kt index cb993034..91ba504c 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/beta/ThreadServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/beta/ThreadServiceAsyncTest.kt @@ -10,8 +10,6 @@ import com.openai.models.beta.assistants.CodeInterpreterTool import com.openai.models.beta.threads.AssistantToolChoiceOption import com.openai.models.beta.threads.ThreadCreateAndRunParams import com.openai.models.beta.threads.ThreadCreateParams -import com.openai.models.beta.threads.ThreadDeleteParams -import com.openai.models.beta.threads.ThreadRetrieveParams import com.openai.models.beta.threads.ThreadUpdateParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -101,10 +99,7 @@ internal class ThreadServiceAsyncTest { .build() val threadServiceAsync = client.beta().threads() - val threadFuture = - threadServiceAsync.retrieve( - ThreadRetrieveParams.builder().threadId("thread_id").build() - ) + val threadFuture = threadServiceAsync.retrieve("thread_id") val thread = threadFuture.get() thread.validate() @@ -158,8 +153,7 @@ internal class ThreadServiceAsyncTest { .build() val threadServiceAsync = client.beta().threads() - val threadDeletedFuture = - threadServiceAsync.delete(ThreadDeleteParams.builder().threadId("thread_id").build()) + val threadDeletedFuture = threadServiceAsync.delete("thread_id") val threadDeleted = threadDeletedFuture.get() threadDeleted.validate() diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/beta/threads/MessageServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/beta/threads/MessageServiceAsyncTest.kt index 14c21f5c..088486d7 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/beta/threads/MessageServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/beta/threads/MessageServiceAsyncTest.kt @@ -8,7 +8,6 @@ import com.openai.core.JsonValue import com.openai.models.beta.assistants.CodeInterpreterTool import com.openai.models.beta.threads.messages.MessageCreateParams import com.openai.models.beta.threads.messages.MessageDeleteParams -import com.openai.models.beta.threads.messages.MessageListParams import com.openai.models.beta.threads.messages.MessageRetrieveParams import com.openai.models.beta.threads.messages.MessageUpdateParams import org.junit.jupiter.api.Test @@ -106,8 +105,7 @@ internal class MessageServiceAsyncTest { .build() val messageServiceAsync = client.beta().threads().messages() - val pageFuture = - messageServiceAsync.list(MessageListParams.builder().threadId("thread_id").build()) + val pageFuture = messageServiceAsync.list("thread_id") val page = pageFuture.get() page.response().validate() diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/beta/threads/RunServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/beta/threads/RunServiceAsyncTest.kt index 70bdc4c7..51885c40 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/beta/threads/RunServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/beta/threads/RunServiceAsyncTest.kt @@ -11,7 +11,6 @@ import com.openai.models.beta.assistants.CodeInterpreterTool import com.openai.models.beta.threads.AssistantToolChoiceOption import com.openai.models.beta.threads.runs.RunCancelParams import com.openai.models.beta.threads.runs.RunCreateParams -import com.openai.models.beta.threads.runs.RunListParams import com.openai.models.beta.threads.runs.RunRetrieveParams import com.openai.models.beta.threads.runs.RunSubmitToolOutputsParams import com.openai.models.beta.threads.runs.RunUpdateParams @@ -200,7 +199,7 @@ internal class RunServiceAsyncTest { .build() val runServiceAsync = client.beta().threads().runs() - val pageFuture = runServiceAsync.list(RunListParams.builder().threadId("thread_id").build()) + val pageFuture = runServiceAsync.list("thread_id") val page = pageFuture.get() page.response().validate() diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/chat/ChatCompletionServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/chat/ChatCompletionServiceAsyncTest.kt index 02928442..693c02d2 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/chat/ChatCompletionServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/chat/ChatCompletionServiceAsyncTest.kt @@ -12,10 +12,8 @@ import com.openai.models.ReasoningEffort import com.openai.models.ResponseFormatText import com.openai.models.chat.completions.ChatCompletionAudioParam import com.openai.models.chat.completions.ChatCompletionCreateParams -import com.openai.models.chat.completions.ChatCompletionDeleteParams import com.openai.models.chat.completions.ChatCompletionDeveloperMessageParam import com.openai.models.chat.completions.ChatCompletionPredictionContent -import com.openai.models.chat.completions.ChatCompletionRetrieveParams import com.openai.models.chat.completions.ChatCompletionStreamOptions import com.openai.models.chat.completions.ChatCompletionTool import com.openai.models.chat.completions.ChatCompletionToolChoiceOption @@ -262,10 +260,7 @@ internal class ChatCompletionServiceAsyncTest { .build() val chatCompletionServiceAsync = client.chat().completions() - val chatCompletionFuture = - chatCompletionServiceAsync.retrieve( - ChatCompletionRetrieveParams.builder().completionId("completion_id").build() - ) + val chatCompletionFuture = chatCompletionServiceAsync.retrieve("completion_id") val chatCompletion = chatCompletionFuture.get() chatCompletion.validate() @@ -320,10 +315,7 @@ internal class ChatCompletionServiceAsyncTest { .build() val chatCompletionServiceAsync = client.chat().completions() - val chatCompletionDeletedFuture = - chatCompletionServiceAsync.delete( - ChatCompletionDeleteParams.builder().completionId("completion_id").build() - ) + val chatCompletionDeletedFuture = chatCompletionServiceAsync.delete("completion_id") val chatCompletionDeleted = chatCompletionDeletedFuture.get() chatCompletionDeleted.validate() diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/chat/completions/MessageServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/chat/completions/MessageServiceAsyncTest.kt index 5fbbb2fa..2c834f1f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/chat/completions/MessageServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/chat/completions/MessageServiceAsyncTest.kt @@ -4,7 +4,6 @@ package com.openai.services.async.chat.completions import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClientAsync -import com.openai.models.chat.completions.messages.MessageListParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -20,10 +19,7 @@ internal class MessageServiceAsyncTest { .build() val messageServiceAsync = client.chat().completions().messages() - val pageFuture = - messageServiceAsync.list( - MessageListParams.builder().completionId("completion_id").build() - ) + val pageFuture = messageServiceAsync.list("completion_id") val page = pageFuture.get() page.response().validate() diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/evals/RunServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/evals/RunServiceAsyncTest.kt index 22e9d8e9..2f621297 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/evals/RunServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/evals/RunServiceAsyncTest.kt @@ -9,7 +9,6 @@ import com.openai.models.evals.runs.CreateEvalJsonlRunDataSource import com.openai.models.evals.runs.RunCancelParams import com.openai.models.evals.runs.RunCreateParams import com.openai.models.evals.runs.RunDeleteParams -import com.openai.models.evals.runs.RunListParams import com.openai.models.evals.runs.RunRetrieveParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -95,7 +94,7 @@ internal class RunServiceAsyncTest { .build() val runServiceAsync = client.evals().runs() - val pageFuture = runServiceAsync.list(RunListParams.builder().evalId("eval_id").build()) + val pageFuture = runServiceAsync.list("eval_id") val page = pageFuture.get() page.response().validate() diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/finetuning/JobServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/finetuning/JobServiceAsyncTest.kt index 69cee8ab..6a9b1aef 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/finetuning/JobServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/finetuning/JobServiceAsyncTest.kt @@ -5,10 +5,14 @@ package com.openai.services.async.finetuning import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClientAsync import com.openai.core.JsonValue -import com.openai.models.finetuning.jobs.JobCancelParams import com.openai.models.finetuning.jobs.JobCreateParams -import com.openai.models.finetuning.jobs.JobListEventsParams -import com.openai.models.finetuning.jobs.JobRetrieveParams +import com.openai.models.finetuning.methods.DpoHyperparameters +import com.openai.models.finetuning.methods.DpoMethod +import com.openai.models.finetuning.methods.ReinforcementHyperparameters +import com.openai.models.finetuning.methods.ReinforcementMethod +import com.openai.models.finetuning.methods.SupervisedHyperparameters +import com.openai.models.finetuning.methods.SupervisedMethod +import com.openai.models.graders.gradermodels.StringCheckGrader import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -55,10 +59,11 @@ internal class JobServiceAsyncTest { ) .method( JobCreateParams.Method.builder() + .type(JobCreateParams.Method.Type.SUPERVISED) .dpo( - JobCreateParams.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - JobCreateParams.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -67,10 +72,35 @@ internal class JobServiceAsyncTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort.DEFAULT + ) + .build() + ) + .build() + ) .supervised( - JobCreateParams.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - JobCreateParams.Method.Supervised.Hyperparameters.builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -78,7 +108,6 @@ internal class JobServiceAsyncTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) .seed(42L) @@ -100,10 +129,7 @@ internal class JobServiceAsyncTest { .build() val jobServiceAsync = client.fineTuning().jobs() - val fineTuningJobFuture = - jobServiceAsync.retrieve( - JobRetrieveParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() - ) + val fineTuningJobFuture = jobServiceAsync.retrieve("ft-AF1WoRqd3aJAHsqc9NY7iL8F") val fineTuningJob = fineTuningJobFuture.get() fineTuningJob.validate() @@ -133,10 +159,7 @@ internal class JobServiceAsyncTest { .build() val jobServiceAsync = client.fineTuning().jobs() - val fineTuningJobFuture = - jobServiceAsync.cancel( - JobCancelParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() - ) + val fineTuningJobFuture = jobServiceAsync.cancel("ft-AF1WoRqd3aJAHsqc9NY7iL8F") val fineTuningJob = fineTuningJobFuture.get() fineTuningJob.validate() @@ -151,12 +174,39 @@ internal class JobServiceAsyncTest { .build() val jobServiceAsync = client.fineTuning().jobs() - val pageFuture = - jobServiceAsync.listEvents( - JobListEventsParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() - ) + val pageFuture = jobServiceAsync.listEvents("ft-AF1WoRqd3aJAHsqc9NY7iL8F") val page = pageFuture.get() page.response().validate() } + + @Test + fun pause() { + val client = + OpenAIOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val jobServiceAsync = client.fineTuning().jobs() + + val fineTuningJobFuture = jobServiceAsync.pause("ft-AF1WoRqd3aJAHsqc9NY7iL8F") + + val fineTuningJob = fineTuningJobFuture.get() + fineTuningJob.validate() + } + + @Test + fun resume() { + val client = + OpenAIOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val jobServiceAsync = client.fineTuning().jobs() + + val fineTuningJobFuture = jobServiceAsync.resume("ft-AF1WoRqd3aJAHsqc9NY7iL8F") + + val fineTuningJob = fineTuningJobFuture.get() + fineTuningJob.validate() + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/finetuning/alpha/GraderServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/finetuning/alpha/GraderServiceAsyncTest.kt new file mode 100644 index 00000000..e6f3f90e --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/finetuning/alpha/GraderServiceAsyncTest.kt @@ -0,0 +1,71 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.async.finetuning.alpha + +import com.openai.TestServerExtension +import com.openai.client.okhttp.OpenAIOkHttpClientAsync +import com.openai.models.finetuning.alpha.graders.GraderRunParams +import com.openai.models.finetuning.alpha.graders.GraderValidateParams +import com.openai.models.graders.gradermodels.StringCheckGrader +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class GraderServiceAsyncTest { + + @Test + fun run() { + val client = + OpenAIOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val graderServiceAsync = client.fineTuning().alpha().graders() + + val responseFuture = + graderServiceAsync.run( + GraderRunParams.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .modelSample("model_sample") + .referenceAnswer("string") + .build() + ) + + val response = responseFuture.get() + response.validate() + } + + @Test + fun validate() { + val client = + OpenAIOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val graderServiceAsync = client.fineTuning().alpha().graders() + + val responseFuture = + graderServiceAsync.validate( + GraderValidateParams.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .build() + ) + + val response = responseFuture.get() + response.validate() + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/finetuning/jobs/CheckpointServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/finetuning/jobs/CheckpointServiceAsyncTest.kt index ff0521c8..75bfa2d2 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/finetuning/jobs/CheckpointServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/finetuning/jobs/CheckpointServiceAsyncTest.kt @@ -4,7 +4,6 @@ package com.openai.services.async.finetuning.jobs import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClientAsync -import com.openai.models.finetuning.jobs.checkpoints.CheckpointListParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -20,12 +19,7 @@ internal class CheckpointServiceAsyncTest { .build() val checkpointServiceAsync = client.fineTuning().jobs().checkpoints() - val pageFuture = - checkpointServiceAsync.list( - CheckpointListParams.builder() - .fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F") - .build() - ) + val pageFuture = checkpointServiceAsync.list("ft-AF1WoRqd3aJAHsqc9NY7iL8F") val page = pageFuture.get() page.response().validate() diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/responses/InputItemServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/responses/InputItemServiceAsyncTest.kt index 241f678d..2c295376 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/responses/InputItemServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/responses/InputItemServiceAsyncTest.kt @@ -4,7 +4,6 @@ package com.openai.services.async.responses import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClientAsync -import com.openai.models.responses.inputitems.InputItemListParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -20,10 +19,7 @@ internal class InputItemServiceAsyncTest { .build() val inputItemServiceAsync = client.responses().inputItems() - val pageFuture = - inputItemServiceAsync.list( - InputItemListParams.builder().responseId("response_id").build() - ) + val pageFuture = inputItemServiceAsync.list("response_id") val page = pageFuture.get() page.response().validate() diff --git a/openai-java-core/src/test/kotlin/com/openai/services/async/vectorstores/FileServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/vectorstores/FileServiceAsyncTest.kt index fdcfc551..b9564abe 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/async/vectorstores/FileServiceAsyncTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/async/vectorstores/FileServiceAsyncTest.kt @@ -9,7 +9,6 @@ import com.openai.models.vectorstores.AutoFileChunkingStrategyParam import com.openai.models.vectorstores.files.FileContentParams import com.openai.models.vectorstores.files.FileCreateParams import com.openai.models.vectorstores.files.FileDeleteParams -import com.openai.models.vectorstores.files.FileListParams import com.openai.models.vectorstores.files.FileRetrieveParams import com.openai.models.vectorstores.files.FileUpdateParams import org.junit.jupiter.api.Test @@ -101,8 +100,7 @@ internal class FileServiceAsyncTest { .build() val fileServiceAsync = client.vectorStores().files() - val pageFuture = - fileServiceAsync.list(FileListParams.builder().vectorStoreId("vector_store_id").build()) + val pageFuture = fileServiceAsync.list("vector_store_id") val page = pageFuture.get() page.response().validate() diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/BatchServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/BatchServiceTest.kt index 04ad53cd..1a798329 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/BatchServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/BatchServiceTest.kt @@ -5,9 +5,7 @@ package com.openai.services.blocking import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClient import com.openai.core.JsonValue -import com.openai.models.batches.BatchCancelParams import com.openai.models.batches.BatchCreateParams -import com.openai.models.batches.BatchRetrieveParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -49,7 +47,7 @@ internal class BatchServiceTest { .build() val batchService = client.batches() - val batch = batchService.retrieve(BatchRetrieveParams.builder().batchId("batch_id").build()) + val batch = batchService.retrieve("batch_id") batch.validate() } @@ -77,7 +75,7 @@ internal class BatchServiceTest { .build() val batchService = client.batches() - val batch = batchService.cancel(BatchCancelParams.builder().batchId("batch_id").build()) + val batch = batchService.cancel("batch_id") batch.validate() } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/EvalServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/EvalServiceTest.kt index 940e0822..61e2c487 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/EvalServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/EvalServiceTest.kt @@ -6,8 +6,6 @@ import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClient import com.openai.core.JsonValue import com.openai.models.evals.EvalCreateParams -import com.openai.models.evals.EvalDeleteParams -import com.openai.models.evals.EvalRetrieveParams import com.openai.models.evals.EvalUpdateParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -74,7 +72,7 @@ internal class EvalServiceTest { .build() val evalService = client.evals() - val eval = evalService.retrieve(EvalRetrieveParams.builder().evalId("eval_id").build()) + val eval = evalService.retrieve("eval_id") eval.validate() } @@ -127,7 +125,7 @@ internal class EvalServiceTest { .build() val evalService = client.evals() - val eval = evalService.delete(EvalDeleteParams.builder().evalId("eval_id").build()) + val eval = evalService.delete("eval_id") eval.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 673fbbb2..ff77adfc 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 @@ -10,11 +10,8 @@ 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.files.FileContentParams import com.openai.models.files.FileCreateParams -import com.openai.models.files.FileDeleteParams import com.openai.models.files.FilePurpose -import com.openai.models.files.FileRetrieveParams import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -54,8 +51,7 @@ internal class FileServiceTest { .build() val fileService = client.files() - val fileObject = - fileService.retrieve(FileRetrieveParams.builder().fileId("file_id").build()) + val fileObject = fileService.retrieve("file_id") fileObject.validate() } @@ -83,7 +79,7 @@ internal class FileServiceTest { .build() val fileService = client.files() - val fileDeleted = fileService.delete(FileDeleteParams.builder().fileId("file_id").build()) + val fileDeleted = fileService.delete("file_id") fileDeleted.validate() } @@ -98,7 +94,7 @@ internal class FileServiceTest { val fileService = client.files() stubFor(get(anyUrl()).willReturn(ok().withBody("abc"))) - val response = fileService.content(FileContentParams.builder().fileId("file_id").build()) + val response = fileService.content("file_id") assertThat(response.body()).hasContent("abc") } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/ModelServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/ModelServiceTest.kt index 4d899a1b..f00b554d 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/ModelServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/ModelServiceTest.kt @@ -4,8 +4,6 @@ package com.openai.services.blocking import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClient -import com.openai.models.models.ModelDeleteParams -import com.openai.models.models.ModelRetrieveParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -21,8 +19,7 @@ internal class ModelServiceTest { .build() val modelService = client.models() - val model = - modelService.retrieve(ModelRetrieveParams.builder().model("gpt-4o-mini").build()) + val model = modelService.retrieve("gpt-4o-mini") model.validate() } @@ -50,10 +47,7 @@ internal class ModelServiceTest { .build() val modelService = client.models() - val modelDeleted = - modelService.delete( - ModelDeleteParams.builder().model("ft:gpt-4o-mini:acemeco:suffix:abc123").build() - ) + val modelDeleted = modelService.delete("ft:gpt-4o-mini:acemeco:suffix:abc123") modelDeleted.validate() } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/ResponseServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/ResponseServiceTest.kt index e783dcb7..e8ae6452 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/ResponseServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/ResponseServiceTest.kt @@ -12,7 +12,6 @@ import com.openai.models.ReasoningEffort import com.openai.models.ResponseFormatText import com.openai.models.responses.FileSearchTool import com.openai.models.responses.ResponseCreateParams -import com.openai.models.responses.ResponseDeleteParams import com.openai.models.responses.ResponseIncludable import com.openai.models.responses.ResponseRetrieveParams import com.openai.models.responses.ResponseTextConfig @@ -190,10 +189,6 @@ internal class ResponseServiceTest { .build() val responseService = client.responses() - responseService.delete( - ResponseDeleteParams.builder() - .responseId("resp_677efb5139a88190b512bc3fef8e535d") - .build() - ) + responseService.delete("resp_677efb5139a88190b512bc3fef8e535d") } } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/UploadServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/UploadServiceTest.kt index 1affd622..26afefc1 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/UploadServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/UploadServiceTest.kt @@ -5,7 +5,6 @@ package com.openai.services.blocking import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClient import com.openai.models.files.FilePurpose -import com.openai.models.uploads.UploadCancelParams import com.openai.models.uploads.UploadCompleteParams import com.openai.models.uploads.UploadCreateParams import org.junit.jupiter.api.Test @@ -45,8 +44,7 @@ internal class UploadServiceTest { .build() val uploadService = client.uploads() - val upload = - uploadService.cancel(UploadCancelParams.builder().uploadId("upload_abc123").build()) + val upload = uploadService.cancel("upload_abc123") upload.validate() } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/VectorStoreServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/VectorStoreServiceTest.kt index 8c12104e..ba6b6886 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/VectorStoreServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/VectorStoreServiceTest.kt @@ -7,8 +7,6 @@ import com.openai.client.okhttp.OpenAIOkHttpClient import com.openai.core.JsonValue import com.openai.models.vectorstores.AutoFileChunkingStrategyParam import com.openai.models.vectorstores.VectorStoreCreateParams -import com.openai.models.vectorstores.VectorStoreDeleteParams -import com.openai.models.vectorstores.VectorStoreRetrieveParams import com.openai.models.vectorstores.VectorStoreSearchParams import com.openai.models.vectorstores.VectorStoreUpdateParams import org.junit.jupiter.api.Test @@ -53,10 +51,7 @@ internal class VectorStoreServiceTest { .build() val vectorStoreService = client.vectorStores() - val vectorStore = - vectorStoreService.retrieve( - VectorStoreRetrieveParams.builder().vectorStoreId("vector_store_id").build() - ) + val vectorStore = vectorStoreService.retrieve("vector_store_id") vectorStore.validate() } @@ -110,10 +105,7 @@ internal class VectorStoreServiceTest { .build() val vectorStoreService = client.vectorStores() - val vectorStoreDeleted = - vectorStoreService.delete( - VectorStoreDeleteParams.builder().vectorStoreId("vector_store_id").build() - ) + val vectorStoreDeleted = vectorStoreService.delete("vector_store_id") vectorStoreDeleted.validate() } 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 index 6948fa2c..b5b60ad2 100644 --- 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 @@ -28,6 +28,7 @@ internal class TranscriptionServiceTest { TranscriptionCreateParams.builder() .file("some content".byteInputStream()) .model(AudioModel.WHISPER_1) + .chunkingStrategyAuto() .addInclude(TranscriptionInclude.LOGPROBS) .language("language") .prompt("prompt") @@ -54,6 +55,7 @@ internal class TranscriptionServiceTest { TranscriptionCreateParams.builder() .file("some content".byteInputStream()) .model(AudioModel.WHISPER_1) + .chunkingStrategyAuto() .addInclude(TranscriptionInclude.LOGPROBS) .language("language") .prompt("prompt") diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/AssistantServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/AssistantServiceTest.kt index d6381fc6..99bf5bf2 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/AssistantServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/AssistantServiceTest.kt @@ -8,8 +8,6 @@ import com.openai.core.JsonValue import com.openai.models.ChatModel import com.openai.models.ReasoningEffort import com.openai.models.beta.assistants.AssistantCreateParams -import com.openai.models.beta.assistants.AssistantDeleteParams -import com.openai.models.beta.assistants.AssistantRetrieveParams import com.openai.models.beta.assistants.AssistantUpdateParams import com.openai.models.beta.assistants.CodeInterpreterTool import org.junit.jupiter.api.Test @@ -91,10 +89,7 @@ internal class AssistantServiceTest { .build() val assistantService = client.beta().assistants() - val assistant = - assistantService.retrieve( - AssistantRetrieveParams.builder().assistantId("assistant_id").build() - ) + val assistant = assistantService.retrieve("assistant_id") assistant.validate() } @@ -169,10 +164,7 @@ internal class AssistantServiceTest { .build() val assistantService = client.beta().assistants() - val assistantDeleted = - assistantService.delete( - AssistantDeleteParams.builder().assistantId("assistant_id").build() - ) + val assistantDeleted = assistantService.delete("assistant_id") assistantDeleted.validate() } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/ThreadServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/ThreadServiceTest.kt index aefa7318..9cba341a 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/ThreadServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/ThreadServiceTest.kt @@ -10,8 +10,6 @@ import com.openai.models.beta.assistants.CodeInterpreterTool import com.openai.models.beta.threads.AssistantToolChoiceOption import com.openai.models.beta.threads.ThreadCreateAndRunParams import com.openai.models.beta.threads.ThreadCreateParams -import com.openai.models.beta.threads.ThreadDeleteParams -import com.openai.models.beta.threads.ThreadRetrieveParams import com.openai.models.beta.threads.ThreadUpdateParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -100,8 +98,7 @@ internal class ThreadServiceTest { .build() val threadService = client.beta().threads() - val thread = - threadService.retrieve(ThreadRetrieveParams.builder().threadId("thread_id").build()) + val thread = threadService.retrieve("thread_id") thread.validate() } @@ -153,8 +150,7 @@ internal class ThreadServiceTest { .build() val threadService = client.beta().threads() - val threadDeleted = - threadService.delete(ThreadDeleteParams.builder().threadId("thread_id").build()) + val threadDeleted = threadService.delete("thread_id") threadDeleted.validate() } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/threads/MessageServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/threads/MessageServiceTest.kt index be07d655..38c0580d 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/threads/MessageServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/threads/MessageServiceTest.kt @@ -8,7 +8,6 @@ import com.openai.core.JsonValue import com.openai.models.beta.assistants.CodeInterpreterTool import com.openai.models.beta.threads.messages.MessageCreateParams import com.openai.models.beta.threads.messages.MessageDeleteParams -import com.openai.models.beta.threads.messages.MessageListParams import com.openai.models.beta.threads.messages.MessageRetrieveParams import com.openai.models.beta.threads.messages.MessageUpdateParams import org.junit.jupiter.api.Test @@ -103,7 +102,7 @@ internal class MessageServiceTest { .build() val messageService = client.beta().threads().messages() - val page = messageService.list(MessageListParams.builder().threadId("thread_id").build()) + val page = messageService.list("thread_id") page.response().validate() } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/threads/RunServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/threads/RunServiceTest.kt index 6394703e..6d094b7d 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/threads/RunServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/threads/RunServiceTest.kt @@ -11,7 +11,6 @@ import com.openai.models.beta.assistants.CodeInterpreterTool import com.openai.models.beta.threads.AssistantToolChoiceOption import com.openai.models.beta.threads.runs.RunCancelParams import com.openai.models.beta.threads.runs.RunCreateParams -import com.openai.models.beta.threads.runs.RunListParams import com.openai.models.beta.threads.runs.RunRetrieveParams import com.openai.models.beta.threads.runs.RunSubmitToolOutputsParams import com.openai.models.beta.threads.runs.RunUpdateParams @@ -195,7 +194,7 @@ internal class RunServiceTest { .build() val runService = client.beta().threads().runs() - val page = runService.list(RunListParams.builder().threadId("thread_id").build()) + val page = runService.list("thread_id") page.response().validate() } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/chat/ChatCompletionServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/chat/ChatCompletionServiceTest.kt index bec6b5bb..9475a0eb 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/chat/ChatCompletionServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/chat/ChatCompletionServiceTest.kt @@ -12,10 +12,8 @@ import com.openai.models.ReasoningEffort import com.openai.models.ResponseFormatText import com.openai.models.chat.completions.ChatCompletionAudioParam import com.openai.models.chat.completions.ChatCompletionCreateParams -import com.openai.models.chat.completions.ChatCompletionDeleteParams import com.openai.models.chat.completions.ChatCompletionDeveloperMessageParam import com.openai.models.chat.completions.ChatCompletionPredictionContent -import com.openai.models.chat.completions.ChatCompletionRetrieveParams import com.openai.models.chat.completions.ChatCompletionStreamOptions import com.openai.models.chat.completions.ChatCompletionTool import com.openai.models.chat.completions.ChatCompletionToolChoiceOption @@ -261,10 +259,7 @@ internal class ChatCompletionServiceTest { .build() val chatCompletionService = client.chat().completions() - val chatCompletion = - chatCompletionService.retrieve( - ChatCompletionRetrieveParams.builder().completionId("completion_id").build() - ) + val chatCompletion = chatCompletionService.retrieve("completion_id") chatCompletion.validate() } @@ -316,10 +311,7 @@ internal class ChatCompletionServiceTest { .build() val chatCompletionService = client.chat().completions() - val chatCompletionDeleted = - chatCompletionService.delete( - ChatCompletionDeleteParams.builder().completionId("completion_id").build() - ) + val chatCompletionDeleted = chatCompletionService.delete("completion_id") chatCompletionDeleted.validate() } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/chat/completions/MessageServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/chat/completions/MessageServiceTest.kt index f591070d..3a1a7f10 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/chat/completions/MessageServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/chat/completions/MessageServiceTest.kt @@ -4,7 +4,6 @@ package com.openai.services.blocking.chat.completions import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClient -import com.openai.models.chat.completions.messages.MessageListParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -20,8 +19,7 @@ internal class MessageServiceTest { .build() val messageService = client.chat().completions().messages() - val page = - messageService.list(MessageListParams.builder().completionId("completion_id").build()) + val page = messageService.list("completion_id") page.response().validate() } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/evals/RunServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/evals/RunServiceTest.kt index 42a3eaaf..ded7e594 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/evals/RunServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/evals/RunServiceTest.kt @@ -9,7 +9,6 @@ import com.openai.models.evals.runs.CreateEvalJsonlRunDataSource import com.openai.models.evals.runs.RunCancelParams import com.openai.models.evals.runs.RunCreateParams import com.openai.models.evals.runs.RunDeleteParams -import com.openai.models.evals.runs.RunListParams import com.openai.models.evals.runs.RunRetrieveParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -93,7 +92,7 @@ internal class RunServiceTest { .build() val runService = client.evals().runs() - val page = runService.list(RunListParams.builder().evalId("eval_id").build()) + val page = runService.list("eval_id") page.response().validate() } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/finetuning/JobServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/finetuning/JobServiceTest.kt index fd91a8c0..662a78c5 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/finetuning/JobServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/finetuning/JobServiceTest.kt @@ -5,10 +5,14 @@ package com.openai.services.blocking.finetuning import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClient import com.openai.core.JsonValue -import com.openai.models.finetuning.jobs.JobCancelParams import com.openai.models.finetuning.jobs.JobCreateParams -import com.openai.models.finetuning.jobs.JobListEventsParams -import com.openai.models.finetuning.jobs.JobRetrieveParams +import com.openai.models.finetuning.methods.DpoHyperparameters +import com.openai.models.finetuning.methods.DpoMethod +import com.openai.models.finetuning.methods.ReinforcementHyperparameters +import com.openai.models.finetuning.methods.ReinforcementMethod +import com.openai.models.finetuning.methods.SupervisedHyperparameters +import com.openai.models.finetuning.methods.SupervisedMethod +import com.openai.models.graders.gradermodels.StringCheckGrader import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -55,10 +59,11 @@ internal class JobServiceTest { ) .method( JobCreateParams.Method.builder() + .type(JobCreateParams.Method.Type.SUPERVISED) .dpo( - JobCreateParams.Method.Dpo.builder() + DpoMethod.builder() .hyperparameters( - JobCreateParams.Method.Dpo.Hyperparameters.builder() + DpoHyperparameters.builder() .batchSizeAuto() .betaAuto() .learningRateMultiplierAuto() @@ -67,10 +72,35 @@ internal class JobServiceTest { ) .build() ) + .reinforcement( + ReinforcementMethod.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .hyperparameters( + ReinforcementHyperparameters.builder() + .batchSizeAuto() + .computeMultiplierAuto() + .evalIntervalAuto() + .evalSamplesAuto() + .learningRateMultiplierAuto() + .nEpochsAuto() + .reasoningEffort( + ReinforcementHyperparameters.ReasoningEffort.DEFAULT + ) + .build() + ) + .build() + ) .supervised( - JobCreateParams.Method.Supervised.builder() + SupervisedMethod.builder() .hyperparameters( - JobCreateParams.Method.Supervised.Hyperparameters.builder() + SupervisedHyperparameters.builder() .batchSizeAuto() .learningRateMultiplierAuto() .nEpochsAuto() @@ -78,7 +108,6 @@ internal class JobServiceTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) .seed(42L) @@ -99,10 +128,7 @@ internal class JobServiceTest { .build() val jobService = client.fineTuning().jobs() - val fineTuningJob = - jobService.retrieve( - JobRetrieveParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() - ) + val fineTuningJob = jobService.retrieve("ft-AF1WoRqd3aJAHsqc9NY7iL8F") fineTuningJob.validate() } @@ -130,10 +156,7 @@ internal class JobServiceTest { .build() val jobService = client.fineTuning().jobs() - val fineTuningJob = - jobService.cancel( - JobCancelParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() - ) + val fineTuningJob = jobService.cancel("ft-AF1WoRqd3aJAHsqc9NY7iL8F") fineTuningJob.validate() } @@ -147,11 +170,36 @@ internal class JobServiceTest { .build() val jobService = client.fineTuning().jobs() - val page = - jobService.listEvents( - JobListEventsParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() - ) + val page = jobService.listEvents("ft-AF1WoRqd3aJAHsqc9NY7iL8F") page.response().validate() } + + @Test + fun pause() { + val client = + OpenAIOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val jobService = client.fineTuning().jobs() + + val fineTuningJob = jobService.pause("ft-AF1WoRqd3aJAHsqc9NY7iL8F") + + fineTuningJob.validate() + } + + @Test + fun resume() { + val client = + OpenAIOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val jobService = client.fineTuning().jobs() + + val fineTuningJob = jobService.resume("ft-AF1WoRqd3aJAHsqc9NY7iL8F") + + fineTuningJob.validate() + } } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/finetuning/alpha/GraderServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/finetuning/alpha/GraderServiceTest.kt new file mode 100644 index 00000000..8d8ac5e3 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/finetuning/alpha/GraderServiceTest.kt @@ -0,0 +1,69 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.services.blocking.finetuning.alpha + +import com.openai.TestServerExtension +import com.openai.client.okhttp.OpenAIOkHttpClient +import com.openai.models.finetuning.alpha.graders.GraderRunParams +import com.openai.models.finetuning.alpha.graders.GraderValidateParams +import com.openai.models.graders.gradermodels.StringCheckGrader +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class GraderServiceTest { + + @Test + fun run() { + val client = + OpenAIOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val graderService = client.fineTuning().alpha().graders() + + val response = + graderService.run( + GraderRunParams.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .modelSample("model_sample") + .referenceAnswer("string") + .build() + ) + + response.validate() + } + + @Test + fun validate() { + val client = + OpenAIOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val graderService = client.fineTuning().alpha().graders() + + val response = + graderService.validate( + GraderValidateParams.builder() + .grader( + StringCheckGrader.builder() + .input("input") + .name("name") + .operation(StringCheckGrader.Operation.EQ) + .reference("reference") + .build() + ) + .build() + ) + + response.validate() + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/finetuning/jobs/CheckpointServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/finetuning/jobs/CheckpointServiceTest.kt index 3ec97abb..0cb543e5 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/finetuning/jobs/CheckpointServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/finetuning/jobs/CheckpointServiceTest.kt @@ -4,7 +4,6 @@ package com.openai.services.blocking.finetuning.jobs import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClient -import com.openai.models.finetuning.jobs.checkpoints.CheckpointListParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -20,12 +19,7 @@ internal class CheckpointServiceTest { .build() val checkpointService = client.fineTuning().jobs().checkpoints() - val page = - checkpointService.list( - CheckpointListParams.builder() - .fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F") - .build() - ) + val page = checkpointService.list("ft-AF1WoRqd3aJAHsqc9NY7iL8F") page.response().validate() } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/responses/InputItemServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/responses/InputItemServiceTest.kt index 7d3f09f0..fa48569e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/responses/InputItemServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/responses/InputItemServiceTest.kt @@ -4,7 +4,6 @@ package com.openai.services.blocking.responses import com.openai.TestServerExtension import com.openai.client.okhttp.OpenAIOkHttpClient -import com.openai.models.responses.inputitems.InputItemListParams import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -20,8 +19,7 @@ internal class InputItemServiceTest { .build() val inputItemService = client.responses().inputItems() - val page = - inputItemService.list(InputItemListParams.builder().responseId("response_id").build()) + val page = inputItemService.list("response_id") page.response().validate() } diff --git a/openai-java-core/src/test/kotlin/com/openai/services/blocking/vectorstores/FileServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/vectorstores/FileServiceTest.kt index ae9bdeeb..2c0f8c7e 100644 --- a/openai-java-core/src/test/kotlin/com/openai/services/blocking/vectorstores/FileServiceTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/services/blocking/vectorstores/FileServiceTest.kt @@ -9,7 +9,6 @@ import com.openai.models.vectorstores.AutoFileChunkingStrategyParam import com.openai.models.vectorstores.files.FileContentParams import com.openai.models.vectorstores.files.FileCreateParams import com.openai.models.vectorstores.files.FileDeleteParams -import com.openai.models.vectorstores.files.FileListParams import com.openai.models.vectorstores.files.FileRetrieveParams import com.openai.models.vectorstores.files.FileUpdateParams import org.junit.jupiter.api.Test @@ -98,8 +97,7 @@ internal class FileServiceTest { .build() val fileService = client.vectorStores().files() - val page = - fileService.list(FileListParams.builder().vectorStoreId("vector_store_id").build()) + val page = fileService.list("vector_store_id") page.response().validate() } diff --git a/openai-java-example/src/main/java/com/openai/example/AssistantAsyncExample.java b/openai-java-example/src/main/java/com/openai/example/AssistantAsyncExample.java index 93a80bb6..78f88231 100644 --- a/openai-java-example/src/main/java/com/openai/example/AssistantAsyncExample.java +++ b/openai-java-example/src/main/java/com/openai/example/AssistantAsyncExample.java @@ -109,18 +109,14 @@ private static CompletableFuture listThreadMessages(OpenAIClientAsync clie .order(MessageListParams.Order.ASC) .build()); return pageFuture.thenComposeAsync(page -> page.autoPager() - .forEach( - currentMessage -> { - System.out.println(currentMessage.role().toString().toUpperCase()); - currentMessage.content().stream() - .flatMap(content -> content.text().stream()) - .forEach(textBlock -> - System.out.println(textBlock.text().value())); - System.out.println(); - - // Keep iterating - return true; - }, - pageFuture.defaultExecutor())); + .subscribe(currentMessage -> { + System.out.println(currentMessage.role().toString().toUpperCase()); + currentMessage.content().stream() + .flatMap(content -> content.text().stream()) + .forEach(textBlock -> + System.out.println(textBlock.text().value())); + System.out.println(); + }) + .onCompleteFuture()); } } diff --git a/openai-java-example/src/main/java/com/openai/example/ModelListAsyncExample.java b/openai-java-example/src/main/java/com/openai/example/ModelListAsyncExample.java index 29376e7b..3233ef08 100644 --- a/openai-java-example/src/main/java/com/openai/example/ModelListAsyncExample.java +++ b/openai-java-example/src/main/java/com/openai/example/ModelListAsyncExample.java @@ -17,13 +17,8 @@ public static void main(String[] args) { CompletableFuture pageFuture = client.models().list(); pageFuture .thenComposeAsync(page -> page.autoPager() - .forEach( - model -> { - System.out.println(model.id()); - // Keep iterating - return true; - }, - pageFuture.defaultExecutor())) + .subscribe(model -> System.out.println(model.id())) + .onCompleteFuture()) .join(); } } diff --git a/openai-java-example/src/main/java/com/openai/example/ResponsesStructuredOutputsExample.java b/openai-java-example/src/main/java/com/openai/example/ResponsesStructuredOutputsExample.java new file mode 100644 index 00000000..9ef89183 --- /dev/null +++ b/openai-java-example/src/main/java/com/openai/example/ResponsesStructuredOutputsExample.java @@ -0,0 +1,73 @@ +package com.openai.example; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.openai.client.OpenAIClient; +import com.openai.client.okhttp.OpenAIOkHttpClient; +import com.openai.models.ChatModel; +import com.openai.models.responses.ResponseCreateParams; +import com.openai.models.responses.StructuredResponseCreateParams; +import java.util.List; + +public final class ResponsesStructuredOutputsExample { + + public static class Person { + @JsonPropertyDescription("The first name and surname of the person.") + public String name; + + public int birthYear; + + @JsonPropertyDescription("The year the person died, or 'present' if the person is living.") + public String deathYear; + + @Override + public String toString() { + return name + " (" + birthYear + '-' + deathYear + ')'; + } + } + + public static class Book { + public String title; + + public Person author; + + @JsonPropertyDescription("The year in which the book was first published.") + public int publicationYear; + + public String genre; + + @JsonIgnore + public String isbn; + + @Override + public String toString() { + return '"' + title + "\" (" + publicationYear + ") [" + genre + "] by " + author; + } + } + + public static class BookList { + public List books; + } + + private ResponsesStructuredOutputsExample() {} + + public static void main(String[] args) { + // Configures using one of: + // - The `OPENAI_API_KEY` environment variable + // - The `OPENAI_BASE_URL` and `AZURE_OPENAI_KEY` environment variables + OpenAIClient client = OpenAIOkHttpClient.fromEnv(); + + StructuredResponseCreateParams createParams = ResponseCreateParams.builder() + .input("List some famous late twentieth century novels.") + .text(BookList.class) + .model(ChatModel.GPT_4O) + .build(); + + client.responses().create(createParams).output().stream() + .flatMap(item -> item.message().stream()) + .flatMap(message -> message.content().stream()) + .flatMap(content -> content.outputText().stream()) + .flatMap(bookList -> bookList.books.stream()) + .forEach(book -> System.out.println(" - " + book)); + } +} diff --git a/openai-java-example/src/main/java/com/openai/example/StructuredOutputsExample.java b/openai-java-example/src/main/java/com/openai/example/StructuredOutputsExample.java index 9d3ce9da..b4af9999 100644 --- a/openai-java-example/src/main/java/com/openai/example/StructuredOutputsExample.java +++ b/openai-java-example/src/main/java/com/openai/example/StructuredOutputsExample.java @@ -1,15 +1,54 @@ package com.openai.example; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; import com.openai.client.OpenAIClient; import com.openai.client.okhttp.OpenAIOkHttpClient; -import com.openai.core.JsonValue; import com.openai.models.ChatModel; -import com.openai.models.ResponseFormatJsonSchema; -import com.openai.models.ResponseFormatJsonSchema.JsonSchema; import com.openai.models.chat.completions.ChatCompletionCreateParams; -import java.util.Map; +import com.openai.models.chat.completions.StructuredChatCompletionCreateParams; +import java.util.List; public final class StructuredOutputsExample { + + public static class Person { + @JsonPropertyDescription("The first name and surname of the person.") + public String name; + + public int birthYear; + + @JsonPropertyDescription("The year the person died, or 'present' if the person is living.") + public String deathYear; + + @Override + public String toString() { + return name + " (" + birthYear + '-' + deathYear + ')'; + } + } + + public static class Book { + public String title; + + public Person author; + + @JsonPropertyDescription("The year in which the book was first published.") + public int publicationYear; + + public String genre; + + @JsonIgnore + public String isbn; + + @Override + public String toString() { + return '"' + title + "\" (" + publicationYear + ") [" + genre + "] by " + author; + } + } + + public static class BookList { + public List books; + } + private StructuredOutputsExample() {} public static void main(String[] args) { @@ -18,26 +57,16 @@ public static void main(String[] args) { // - The `OPENAI_BASE_URL` and `AZURE_OPENAI_KEY` environment variables OpenAIClient client = OpenAIOkHttpClient.fromEnv(); - // TODO: Update this once we support extracting JSON schemas from Java classes - JsonSchema.Schema schema = JsonSchema.Schema.builder() - .putAdditionalProperty("type", JsonValue.from("object")) - .putAdditionalProperty( - "properties", JsonValue.from(Map.of("employees", Map.of("items", Map.of("type", "string"))))) - .build(); - ChatCompletionCreateParams createParams = ChatCompletionCreateParams.builder() + StructuredChatCompletionCreateParams createParams = ChatCompletionCreateParams.builder() .model(ChatModel.GPT_4O_MINI) .maxCompletionTokens(2048) - .responseFormat(ResponseFormatJsonSchema.builder() - .jsonSchema(JsonSchema.builder() - .name("employee-list") - .schema(schema) - .build()) - .build()) - .addUserMessage("Who works at OpenAI?") + .responseFormat(BookList.class) + .addUserMessage("List some famous late twentieth century novels.") .build(); client.chat().completions().create(createParams).choices().stream() .flatMap(choice -> choice.message().content().stream()) - .forEach(System.out::println); + .flatMap(bookList -> bookList.books.stream()) + .forEach(book -> System.out.println(" - " + book)); } } diff --git a/openai-java-example/src/main/java/com/openai/example/StructuredOutputsAsyncExample.java b/openai-java-example/src/main/java/com/openai/example/StructuredOutputsRawAsyncExample.java similarity index 91% rename from openai-java-example/src/main/java/com/openai/example/StructuredOutputsAsyncExample.java rename to openai-java-example/src/main/java/com/openai/example/StructuredOutputsRawAsyncExample.java index a645f6cb..f726e0cf 100644 --- a/openai-java-example/src/main/java/com/openai/example/StructuredOutputsAsyncExample.java +++ b/openai-java-example/src/main/java/com/openai/example/StructuredOutputsRawAsyncExample.java @@ -9,8 +9,8 @@ import com.openai.models.chat.completions.ChatCompletionCreateParams; import java.util.Map; -public final class StructuredOutputsAsyncExample { - private StructuredOutputsAsyncExample() {} +public final class StructuredOutputsRawAsyncExample { + private StructuredOutputsRawAsyncExample() {} public static void main(String[] args) { // Configures using one of: @@ -18,7 +18,6 @@ public static void main(String[] args) { // - The `OPENAI_BASE_URL` and `AZURE_OPENAI_KEY` environment variables OpenAIClientAsync client = OpenAIOkHttpClientAsync.fromEnv(); - // TODO: Update this once we support extracting JSON schemas from Java classes JsonSchema.Schema schema = JsonSchema.Schema.builder() .putAdditionalProperty("type", JsonValue.from("object")) .putAdditionalProperty( diff --git a/openai-java-example/src/main/java/com/openai/example/StructuredOutputsRawExample.java b/openai-java-example/src/main/java/com/openai/example/StructuredOutputsRawExample.java new file mode 100644 index 00000000..3b9ff03a --- /dev/null +++ b/openai-java-example/src/main/java/com/openai/example/StructuredOutputsRawExample.java @@ -0,0 +1,42 @@ +package com.openai.example; + +import com.openai.client.OpenAIClient; +import com.openai.client.okhttp.OpenAIOkHttpClient; +import com.openai.core.JsonValue; +import com.openai.models.ChatModel; +import com.openai.models.ResponseFormatJsonSchema; +import com.openai.models.ResponseFormatJsonSchema.JsonSchema; +import com.openai.models.chat.completions.ChatCompletionCreateParams; +import java.util.Map; + +public final class StructuredOutputsRawExample { + private StructuredOutputsRawExample() {} + + public static void main(String[] args) { + // Configures using one of: + // - The `OPENAI_API_KEY` environment variable + // - The `OPENAI_BASE_URL` and `AZURE_OPENAI_KEY` environment variables + OpenAIClient client = OpenAIOkHttpClient.fromEnv(); + + JsonSchema.Schema schema = JsonSchema.Schema.builder() + .putAdditionalProperty("type", JsonValue.from("object")) + .putAdditionalProperty( + "properties", JsonValue.from(Map.of("employees", Map.of("items", Map.of("type", "string"))))) + .build(); + ChatCompletionCreateParams createParams = ChatCompletionCreateParams.builder() + .model(ChatModel.GPT_4O_MINI) + .maxCompletionTokens(2048) + .responseFormat(ResponseFormatJsonSchema.builder() + .jsonSchema(JsonSchema.builder() + .name("employee-list") + .schema(schema) + .build()) + .build()) + .addUserMessage("Who works at OpenAI?") + .build(); + + client.chat().completions().create(createParams).choices().stream() + .flatMap(choice -> choice.message().content().stream()) + .forEach(System.out::println); + } +}