From d2438923c2f53a76879464ab3816732b5c4b5718 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 17:44:10 +0000 Subject: [PATCH 01/17] feat(api): Add reinforcement fine-tuning api support --- .stats.yml | 8 +- .../kotlin/com/openai/client/OpenAIClient.kt | 5 + .../com/openai/client/OpenAIClientAsync.kt | 5 + .../openai/client/OpenAIClientAsyncImpl.kt | 14 + .../com/openai/client/OpenAIClientImpl.kt | 12 + .../openai/models/evals/EvalCreateParams.kt | 1909 +++++-------- .../openai/models/evals/EvalCreateResponse.kt | 2097 ++++++-------- .../openai/models/evals/EvalListResponse.kt | 2097 ++++++-------- .../models/evals/EvalRetrieveResponse.kt | 2097 ++++++-------- .../openai/models/evals/EvalUpdateResponse.kt | 2097 ++++++-------- .../alpha/graders/GraderRunParams.kt | 1145 ++++++++ .../alpha/graders/GraderRunResponse.kt | 1772 ++++++++++++ .../alpha/graders/GraderValidateParams.kt | 747 +++++ .../alpha/graders/GraderValidateResponse.kt | 478 ++++ .../models/finetuning/jobs/FineTuningJob.kt | 2427 +---------------- .../models/finetuning/jobs/JobCreateParams.kt | 2427 +---------------- .../models/finetuning/jobs/JobPauseParams.kt | 232 ++ .../models/finetuning/jobs/JobResumeParams.kt | 232 ++ .../finetuning/methods/DpoHyperparameters.kt | 1050 +++++++ .../models/finetuning/methods/DpoMethod.kt | 166 ++ .../methods/ReinforcementHyperparameters.kt | 1703 ++++++++++++ .../finetuning/methods/ReinforcementMethod.kt | 541 ++++ .../methods/SupervisedHyperparameters.kt | 832 ++++++ .../finetuning/methods/SupervisedMethod.kt | 167 ++ .../gradermodels/LabelModelGrader.kt} | 36 +- .../graders/gradermodels/MultiGrader.kt | 391 +++ .../graders/gradermodels/PythonGrader.kt | 282 ++ .../graders/gradermodels/ScoreModelGrader.kt | 1313 +++++++++ .../gradermodels/StringCheckGrader.kt} | 34 +- .../gradermodels/TextSimilarityGrader.kt} | 119 +- .../services/async/FineTuningServiceAsync.kt | 10 + .../async/FineTuningServiceAsyncImpl.kt | 24 + .../services/async/GraderServiceAsync.kt | 23 + .../services/async/GraderServiceAsyncImpl.kt | 33 + .../async/finetuning/AlphaServiceAsync.kt | 21 + .../async/finetuning/AlphaServiceAsyncImpl.kt | 31 + .../async/finetuning/JobServiceAsync.kt | 52 + .../async/finetuning/JobServiceAsyncImpl.kt | 76 + .../async/finetuning/MethodServiceAsync.kt | 16 + .../finetuning/MethodServiceAsyncImpl.kt | 18 + .../finetuning/alpha/GraderServiceAsync.kt | 78 + .../alpha/GraderServiceAsyncImpl.kt | 113 + .../async/graders/GraderModelServiceAsync.kt | 17 + .../graders/GraderModelServiceAsyncImpl.kt | 18 + .../services/blocking/FineTuningService.kt | 10 + .../blocking/FineTuningServiceImpl.kt | 24 + .../openai/services/blocking/GraderService.kt | 21 + .../services/blocking/GraderServiceImpl.kt | 31 + .../blocking/finetuning/AlphaService.kt | 21 + .../blocking/finetuning/AlphaServiceImpl.kt | 31 + .../blocking/finetuning/JobService.kt | 50 + .../blocking/finetuning/JobServiceImpl.kt | 64 + .../blocking/finetuning/MethodService.kt | 14 + .../blocking/finetuning/MethodServiceImpl.kt | 18 + .../finetuning/alpha/GraderService.kt | 72 + .../finetuning/alpha/GraderServiceImpl.kt | 103 + .../blocking/graders/GraderModelService.kt | 16 + .../graders/GraderModelServiceImpl.kt | 18 + .../models/evals/EvalCreateResponseTest.kt | 27 +- .../models/evals/EvalLabelModelGraderTest.kt | 69 - .../models/evals/EvalListPageResponseTest.kt | 25 +- .../models/evals/EvalListResponseTest.kt | 27 +- .../models/evals/EvalRetrieveResponseTest.kt | 27 +- .../models/evals/EvalStringCheckGraderTest.kt | 47 - .../evals/EvalTextSimilarityGraderTest.kt | 51 - .../models/evals/EvalUpdateResponseTest.kt | 27 +- .../alpha/graders/GraderRunParamsTest.kt | 60 + .../alpha/graders/GraderRunResponseTest.kt | 172 ++ .../alpha/graders/GraderValidateParamsTest.kt | 53 + .../graders/GraderValidateResponseTest.kt | 63 + .../finetuning/jobs/FineTuningJobTest.kt | 112 +- .../finetuning/jobs/JobCreateParamsTest.kt | 112 +- .../jobs/JobListPageResponseTest.kt | 116 +- .../finetuning/jobs/JobPauseParamsTest.kt | 23 + .../finetuning/jobs/JobResumeParamsTest.kt | 24 + .../methods/DpoHyperparametersTest.kt | 48 + .../finetuning/methods/DpoMethodTest.kt | 60 + .../ReinforcementHyperparametersTest.kt | 63 + .../methods/ReinforcementMethodTest.kt | 97 + .../methods/SupervisedHyperparametersTest.kt | 47 + .../methods/SupervisedMethodTest.kt | 57 + .../gradermodels/LabelModelGraderTest.kt | 69 + .../graders/gradermodels/MultiGraderTest.kt | 91 + .../graders/gradermodels/PythonGraderTest.kt | 36 + .../gradermodels/ScoreModelGraderTest.kt | 72 + .../gradermodels/StringCheckGraderTest.kt | 47 + .../gradermodels/TextSimilarityGraderTest.kt | 48 + .../com/openai/services/ErrorHandlingTest.kt | 340 ++- .../async/finetuning/JobServiceAsyncTest.kt | 80 +- .../alpha/GraderServiceAsyncTest.kt | 71 + .../blocking/finetuning/JobServiceTest.kt | 78 +- .../finetuning/alpha/GraderServiceTest.kt | 69 + 92 files changed, 18330 insertions(+), 11536 deletions(-) create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunParams.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunResponse.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateParams.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateResponse.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobPauseParams.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobResumeParams.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/DpoHyperparameters.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/DpoMethod.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/ReinforcementHyperparameters.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/ReinforcementMethod.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/SupervisedHyperparameters.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/SupervisedMethod.kt rename openai-java-core/src/main/kotlin/com/openai/models/{evals/EvalLabelModelGrader.kt => graders/gradermodels/LabelModelGrader.kt} (97%) create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/MultiGrader.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/PythonGrader.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/graders/gradermodels/ScoreModelGrader.kt rename openai-java-core/src/main/kotlin/com/openai/models/{evals/EvalStringCheckGrader.kt => graders/gradermodels/StringCheckGrader.kt} (92%) rename openai-java-core/src/main/kotlin/com/openai/models/{evals/EvalTextSimilarityGrader.kt => graders/gradermodels/TextSimilarityGrader.kt} (82%) create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/async/GraderServiceAsync.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/async/GraderServiceAsyncImpl.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/AlphaServiceAsync.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/AlphaServiceAsyncImpl.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/MethodServiceAsync.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/MethodServiceAsyncImpl.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/alpha/GraderServiceAsync.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/alpha/GraderServiceAsyncImpl.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/async/graders/GraderModelServiceAsync.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/async/graders/GraderModelServiceAsyncImpl.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/blocking/GraderService.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/blocking/GraderServiceImpl.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/AlphaService.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/AlphaServiceImpl.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/MethodService.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/MethodServiceImpl.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/alpha/GraderService.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/alpha/GraderServiceImpl.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/blocking/graders/GraderModelService.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/services/blocking/graders/GraderModelServiceImpl.kt delete mode 100644 openai-java-core/src/test/kotlin/com/openai/models/evals/EvalLabelModelGraderTest.kt delete mode 100644 openai-java-core/src/test/kotlin/com/openai/models/evals/EvalStringCheckGraderTest.kt delete mode 100644 openai-java-core/src/test/kotlin/com/openai/models/evals/EvalTextSimilarityGraderTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunParamsTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderRunResponseTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateParamsTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/finetuning/alpha/graders/GraderValidateResponseTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobPauseParamsTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/finetuning/jobs/JobResumeParamsTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/DpoHyperparametersTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/DpoMethodTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/ReinforcementHyperparametersTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/ReinforcementMethodTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/SupervisedHyperparametersTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/finetuning/methods/SupervisedMethodTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/LabelModelGraderTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/MultiGraderTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/PythonGraderTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/ScoreModelGraderTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/StringCheckGraderTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/graders/gradermodels/TextSimilarityGraderTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/services/async/finetuning/alpha/GraderServiceAsyncTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/services/blocking/finetuning/alpha/GraderServiceTest.kt diff --git a/.stats.yml b/.stats.yml index 089abe5d8..5a1f2ff0e 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-794a6ed3c3d3d77887564755168056af8a426b17cf1ec721e3a300503dc22a41.yml +openapi_spec_hash: 25a81c220713cd5b0bafc221d1dfa79a +config_hash: 0b768ed1b56c6d82816f0fa40dc4aaf5 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 3995c9ea9..e990bc66e 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 2a2d5894c..aff5f61eb 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 2bbeb00bb..5e2719a6b 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 17ad13b4f..c79edfecb 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/models/evals/EvalCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalCreateParams.kt index 08525daa0..34c286275 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 @@ -196,8 +200,13 @@ private constructor( body.customDataSourceConfig(itemSchema) } - /** Alias for calling [dataSourceConfig] with `DataSourceConfig.ofLogs(logs)`. */ - fun dataSourceConfig(logs: DataSourceConfig.Logs) = apply { body.dataSourceConfig(logs) } + /** + * Alias for calling [dataSourceConfig] with + * `DataSourceConfig.ofStoredCompletions(storedCompletions)`. + */ + fun dataSourceConfig(storedCompletions: DataSourceConfig.StoredCompletions) = apply { + body.dataSourceConfig(storedCompletions) + } /** A list of graders for all eval runs in this group. */ fun testingCriteria(testingCriteria: List) = apply { @@ -235,7 +244,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 +252,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) } @@ -602,9 +611,12 @@ private constructor( fun customDataSourceConfig(itemSchema: DataSourceConfig.Custom.ItemSchema) = dataSourceConfig(DataSourceConfig.Custom.builder().itemSchema(itemSchema).build()) - /** Alias for calling [dataSourceConfig] with `DataSourceConfig.ofLogs(logs)`. */ - fun dataSourceConfig(logs: DataSourceConfig.Logs) = - dataSourceConfig(DataSourceConfig.ofLogs(logs)) + /** + * Alias for calling [dataSourceConfig] with + * `DataSourceConfig.ofStoredCompletions(storedCompletions)`. + */ + fun dataSourceConfig(storedCompletions: DataSourceConfig.StoredCompletions) = + dataSourceConfig(DataSourceConfig.ofStoredCompletions(storedCompletions)) /** A list of graders for all eval runs in this group. */ fun testingCriteria(testingCriteria: List) = @@ -644,14 +656,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)`. */ @@ -800,7 +812,7 @@ private constructor( class DataSourceConfig private constructor( private val custom: Custom? = null, - private val logs: Logs? = null, + private val storedCompletions: StoredCompletions? = null, private val _json: JsonValue? = null, ) { @@ -816,11 +828,12 @@ 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. */ - fun logs(): Optional = Optional.ofNullable(logs) + fun storedCompletions(): Optional = + Optional.ofNullable(storedCompletions) fun isCustom(): Boolean = custom != null - fun isLogs(): Boolean = logs != null + fun isStoredCompletions(): Boolean = storedCompletions != null /** * A CustomDataSourceConfig object that defines the schema for the data source used for the @@ -834,14 +847,15 @@ 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. */ - fun asLogs(): Logs = logs.getOrThrow("logs") + 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) } @@ -858,8 +872,8 @@ private constructor( custom.validate() } - override fun visitLogs(logs: Logs) { - logs.validate() + override fun visitStoredCompletions(storedCompletions: StoredCompletions) { + storedCompletions.validate() } } ) @@ -886,7 +900,8 @@ private constructor( object : Visitor { override fun visitCustom(custom: Custom) = custom.validity() - override fun visitLogs(logs: Logs) = logs.validity() + override fun visitStoredCompletions(storedCompletions: StoredCompletions) = + storedCompletions.validity() override fun unknown(json: JsonValue?) = 0 } @@ -897,15 +912,16 @@ 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 && 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, 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") } @@ -925,7 +941,9 @@ 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. */ - @JvmStatic fun ofLogs(logs: Logs) = DataSourceConfig(logs = logs) + @JvmStatic + fun ofStoredCompletions(storedCompletions: StoredCompletions) = + DataSourceConfig(storedCompletions = storedCompletions) } /** @@ -947,7 +965,7 @@ 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. */ - fun visitLogs(logs: Logs): T + fun visitStoredCompletions(storedCompletions: StoredCompletions): T /** * Maps an unknown variant of [DataSourceConfig] to a value of type [T]. @@ -976,9 +994,9 @@ private constructor( DataSourceConfig(custom = it, _json = json) } ?: DataSourceConfig(_json = json) } - "logs" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { - DataSourceConfig(logs = it, _json = json) + "stored_completions" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + DataSourceConfig(storedCompletions = it, _json = json) } ?: DataSourceConfig(_json = json) } } @@ -996,7 +1014,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") } @@ -1376,7 +1395,7 @@ 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. */ - class Logs + class StoredCompletions private constructor( private val type: JsonValue, private val metadata: JsonField, @@ -1392,11 +1411,11 @@ private constructor( ) : this(type, metadata, mutableMapOf()) /** - * The type of data source. Always `logs`. + * The type of data source. Always `stored_completions`. * * Expected to always return the following: * ```java - * JsonValue.from("logs") + * JsonValue.from("stored_completions") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1405,7 +1424,7 @@ private constructor( @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** - * Metadata filters for the logs data source. + * 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). @@ -1436,22 +1455,24 @@ private constructor( companion object { - /** Returns a mutable builder for constructing an instance of [Logs]. */ + /** + * Returns a mutable builder for constructing an instance of [StoredCompletions]. + */ @JvmStatic fun builder() = Builder() } - /** A builder for [Logs]. */ + /** A builder for [StoredCompletions]. */ class Builder internal constructor() { - private var type: JsonValue = JsonValue.from("logs") + private var type: JsonValue = JsonValue.from("stored_completions") private var metadata: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(logs: Logs) = apply { - type = logs.type - metadata = logs.metadata - additionalProperties = logs.additionalProperties.toMutableMap() + internal fun from(storedCompletions: StoredCompletions) = apply { + type = storedCompletions.type + metadata = storedCompletions.metadata + additionalProperties = storedCompletions.additionalProperties.toMutableMap() } /** @@ -1460,7 +1481,7 @@ private constructor( * It is usually unnecessary to call this method because the field defaults to the * following: * ```java - * JsonValue.from("logs") + * JsonValue.from("stored_completions") * ``` * * This method is primarily for setting the field to an undocumented or not yet @@ -1468,7 +1489,7 @@ private constructor( */ fun type(type: JsonValue) = apply { this.type = type } - /** Metadata filters for the logs data source. */ + /** Metadata filters for the stored completions data source. */ fun metadata(metadata: Metadata) = metadata(JsonField.of(metadata)) /** @@ -1503,22 +1524,23 @@ private constructor( } /** - * Returns an immutable instance of [Logs]. + * Returns an immutable instance of [StoredCompletions]. * * Further updates to this [Builder] will not mutate the returned instance. */ - fun build(): Logs = Logs(type, metadata, additionalProperties.toMutableMap()) + fun build(): StoredCompletions = + StoredCompletions(type, metadata, additionalProperties.toMutableMap()) } private var validated: Boolean = false - fun validate(): Logs = apply { + fun validate(): StoredCompletions = apply { if (validated) { return@apply } _type().let { - if (it != JsonValue.from("logs")) { + if (it != JsonValue.from("stored_completions")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") } } @@ -1542,10 +1564,10 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - type.let { if (it == JsonValue.from("logs")) 1 else 0 } + + type.let { if (it == JsonValue.from("stored_completions")) 1 else 0 } + (metadata.asKnown().getOrNull()?.validity() ?: 0) - /** Metadata filters for the logs data source. */ + /** Metadata filters for the stored completions data source. */ class Metadata @JsonCreator private constructor( @@ -1657,7 +1679,7 @@ private constructor( return true } - return /* spotless:off */ other is Logs && type == other.type && metadata == other.metadata && additionalProperties == other.additionalProperties /* spotless:on */ + return /* spotless:off */ other is StoredCompletions && type == other.type && metadata == other.metadata && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ @@ -1667,7 +1689,7 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "Logs{type=$type, metadata=$metadata, additionalProperties=$additionalProperties}" + "StoredCompletions{type=$type, metadata=$metadata, additionalProperties=$additionalProperties}" } } @@ -1679,8 +1701,8 @@ private constructor( class TestingCriterion private constructor( private val labelModel: LabelModel? = null, - private val stringCheck: EvalStringCheckGrader? = null, - private val textSimilarity: EvalTextSimilarityGrader? = 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, @@ -1696,11 +1718,10 @@ private constructor( * A StringCheckGrader object that performs a string comparison between input and reference * using a specified operation. */ - fun stringCheck(): Optional = Optional.ofNullable(stringCheck) + fun stringCheck(): Optional = Optional.ofNullable(stringCheck) /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun textSimilarity(): Optional = - Optional.ofNullable(textSimilarity) + fun textSimilarity(): Optional = Optional.ofNullable(textSimilarity) /** A PythonGrader object that runs a python script on the input. */ fun python(): Optional = Optional.ofNullable(python) @@ -1728,11 +1749,10 @@ private constructor( * A StringCheckGrader object that performs a string comparison between input and reference * using a specified operation. */ - fun asStringCheck(): EvalStringCheckGrader = stringCheck.getOrThrow("stringCheck") + fun asStringCheck(): StringCheckGrader = stringCheck.getOrThrow("stringCheck") /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun asTextSimilarity(): EvalTextSimilarityGrader = - textSimilarity.getOrThrow("textSimilarity") + fun asTextSimilarity(): TextSimilarity = textSimilarity.getOrThrow("textSimilarity") /** A PythonGrader object that runs a python script on the input. */ fun asPython(): Python = python.getOrThrow("python") @@ -1765,11 +1785,11 @@ private constructor( labelModel.validate() } - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) { + override fun visitStringCheck(stringCheck: StringCheckGrader) { stringCheck.validate() } - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) { + override fun visitTextSimilarity(textSimilarity: TextSimilarity) { textSimilarity.validate() } @@ -1805,10 +1825,10 @@ private constructor( object : Visitor { override fun visitLabelModel(labelModel: LabelModel) = labelModel.validity() - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) = + override fun visitStringCheck(stringCheck: StringCheckGrader) = stringCheck.validity() - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = + override fun visitTextSimilarity(textSimilarity: TextSimilarity) = textSimilarity.validity() override fun visitPython(python: Python) = python.validity() @@ -1854,12 +1874,12 @@ private constructor( * reference using a specified operation. */ @JvmStatic - fun ofStringCheck(stringCheck: EvalStringCheckGrader) = + fun ofStringCheck(stringCheck: StringCheckGrader) = TestingCriterion(stringCheck = stringCheck) /** A TextSimilarityGrader object which grades text based on similarity metrics. */ @JvmStatic - fun ofTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = + fun ofTextSimilarity(textSimilarity: TextSimilarity) = TestingCriterion(textSimilarity = textSimilarity) /** A PythonGrader object that runs a python script on the input. */ @@ -1886,10 +1906,10 @@ private constructor( * A StringCheckGrader object that performs a string comparison between input and * reference using a specified operation. */ - fun visitStringCheck(stringCheck: EvalStringCheckGrader): T + fun visitStringCheck(stringCheck: StringCheckGrader): T /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader): T + fun visitTextSimilarity(textSimilarity: TextSimilarity): T /** A PythonGrader object that runs a python script on the input. */ fun visitPython(python: Python): T @@ -1925,14 +1945,14 @@ private constructor( } ?: TestingCriterion(_json = json) } "string_check" -> { - return tryDeserialize(node, jacksonTypeRef())?.let { + 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) + return tryDeserialize(node, jacksonTypeRef())?.let { + TestingCriterion(textSimilarity = it, _json = json) + } ?: TestingCriterion(_json = json) } "python" -> { return tryDeserialize(node, jacksonTypeRef())?.let { @@ -3819,31 +3839,63 @@ private constructor( "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 + /** 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 source: JsonField, + private val reference: JsonField, private val type: JsonValue, - private val imageTag: JsonField, private val passThreshold: JsonField, private val additionalProperties: MutableMap, ) { @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("source") + @JsonProperty("reference") @ExcludeMissing - source: JsonField = JsonMissing.of(), + reference: 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()) + ) : 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 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") + + /** + * 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") /** * The name of the grader. @@ -3855,20 +3907,20 @@ private constructor( fun name(): String = name.getRequired("name") /** - * The source code of the python script. + * 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 source(): String = source.getRequired("source") + fun reference(): String = reference.getRequired("reference") /** - * The object type, which is always `python`. + * The type of grader. * * Expected to always return the following: * ```java - * JsonValue.from("python") + * JsonValue.from("text_similarity") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -3877,42 +3929,48 @@ private constructor( @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** - * The image tag to use for the python script. + * 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 imageTag(): Optional = imageTag.getOptional("image_tag") + fun passThreshold(): Double = passThreshold.getRequired("pass_threshold") /** - * The threshold for the score. + * 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 passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + @JsonProperty("evaluation_metric") + @ExcludeMissing + fun _evaluationMetric(): JsonField = + evaluationMetric /** - * Returns the raw JSON value of [name]. + * Returns the raw JSON value of [input]. * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JsonProperty("input") @ExcludeMissing fun _input(): JsonField = input /** - * Returns the raw JSON value of [source]. + * Returns the raw JSON value of [name]. * - * Unlike [source], 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("source") @ExcludeMissing fun _source(): JsonField = source + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name /** - * Returns the raw JSON value of [imageTag]. + * Returns the raw JSON value of [reference]. * - * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected * type. */ - @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag + @JsonProperty("reference") + @ExcludeMissing + fun _reference(): JsonField = reference /** * Returns the raw JSON value of [passThreshold]. @@ -3939,37 +3997,73 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [Python]. + * Returns a mutable builder for constructing an instance of [TextSimilarity]. * * The following fields are required: * ```java + * .evaluationMetric() + * .input() * .name() - * .source() + * .reference() + * .passThreshold() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [Python]. */ + /** A builder for [TextSimilarity]. */ class Builder internal constructor() { + private var evaluationMetric: JsonField? = + null + private var input: JsonField? = null 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 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(python: Python) = apply { - name = python.name - source = python.source - type = python.type - imageTag = python.imageTag - passThreshold = python.passThreshold - additionalProperties = python.additionalProperties.toMutableMap() + 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() } + /** + * 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)) + + /** + * Sets [Builder.evaluationMetric] to an arbitrary JSON value. + * + * 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 evaluationMetric( + evaluationMetric: JsonField + ) = apply { this.evaluationMetric = evaluationMetric } + + /** The text being graded. */ + fun input(input: String) = input(JsonField.of(input)) + + /** + * 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 } + /** The name of the grader. */ fun name(name: String) = name(JsonField.of(name)) @@ -3982,17 +4076,17 @@ private constructor( */ fun name(name: JsonField) = apply { this.name = name } - /** The source code of the python script. */ - fun source(source: String) = source(JsonField.of(source)) + /** The text being graded against. */ + fun reference(reference: String) = reference(JsonField.of(reference)) /** - * Sets [Builder.source] to an arbitrary JSON value. + * Sets [Builder.reference] to an arbitrary JSON value. * - * You should usually call [Builder.source] with a well-typed [String] 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 source(source: JsonField) = apply { this.source = source } + fun reference(reference: JsonField) = apply { this.reference = reference } /** * Sets the field to an arbitrary JSON value. @@ -4000,7 +4094,7 @@ private constructor( * It is usually unnecessary to call this method because the field defaults to the * following: * ```java - * JsonValue.from("python") + * JsonValue.from("text_similarity") * ``` * * This method is primarily for setting the field to an undocumented or not yet @@ -4008,18 +4102,6 @@ private constructor( */ 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)) @@ -4058,44 +4140,49 @@ private constructor( } /** - * Returns an immutable instance of [Python]. + * 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() - * .source() + * .reference() + * .passThreshold() * ``` * * @throws IllegalStateException if any required field is unset. */ - fun build(): Python = - Python( + fun build(): TextSimilarity = + TextSimilarity( + checkRequired("evaluationMetric", evaluationMetric), + checkRequired("input", input), checkRequired("name", name), - checkRequired("source", source), + checkRequired("reference", reference), type, - imageTag, - passThreshold, + checkRequired("passThreshold", passThreshold), additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): Python = apply { + fun validate(): TextSimilarity = apply { if (validated) { return@apply } + evaluationMetric().validate() + input() name() - source() + reference() _type().let { - if (it != JsonValue.from("python")) { + if (it != JsonValue.from("text_similarity")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - imageTag() passThreshold() validated = true } @@ -4116,10 +4203,11 @@ private constructor( */ @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) + + (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 equals(other: Any?): Boolean { @@ -4127,84 +4215,77 @@ private constructor( 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 TextSimilarity && evaluationMetric == other.evaluationMetric && input == other.input && name == other.name && reference == other.reference && type == other.type && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(evaluationMetric, input, name, reference, type, 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}" + "TextSimilarity{evaluationMetric=$evaluationMetric, input=$input, name=$name, reference=$reference, type=$type, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - class ScoreModel + /** A PythonGrader object that runs a python script on the input. */ + class Python private constructor( - private val input: JsonField>, - private val model: JsonField, private val name: JsonField, + private val source: JsonField, private val type: JsonValue, + private val imageTag: JsonField, 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("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(), - @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(name, source, type, imageTag, passThreshold, 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") + fun toPythonGrader(): PythonGrader = + PythonGrader.builder() + .name(name) + .source(source) + .type(type) + .imageTag(imageTag) + .build() /** - * The model to use for the evaluation. + * 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 model(): String = model.getRequired("model") + fun name(): String = name.getRequired("name") /** - * The name of the grader. + * 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 name(): String = name.getRequired("name") + fun source(): String = source.getRequired("source") /** - * The object type, which is always `score_model`. + * The object type, which is always `python`. * * Expected to always return the following: * ```java - * JsonValue.from("score_model") + * JsonValue.from("python") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -4213,46 +4294,42 @@ private constructor( @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** - * The threshold for the score. + * 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 passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + fun imageTag(): Optional = imageTag.getOptional("image_tag") /** - * The range of the score. Defaults to `[0, 1]`. + * 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 range(): Optional> = range.getOptional("range") - - /** The sampling parameters for the model. */ - @JsonProperty("sampling_params") - @ExcludeMissing - fun _samplingParams(): JsonValue = samplingParams + fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") /** - * Returns the raw JSON value of [input]. + * Returns the raw JSON value of [name]. * - * Unlike [input], 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("input") @ExcludeMissing fun _input(): JsonField> = input + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name /** - * Returns the raw JSON value of [model]. + * Returns the raw JSON value of [source]. * - * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source /** - * Returns the raw JSON value of [name]. + * Returns the raw JSON value of [imageTag]. * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected + * type. */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag /** * Returns the raw JSON value of [passThreshold]. @@ -4264,13 +4341,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) @@ -4286,80 +4356,37 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [ScoreModel]. + * Returns a mutable builder for constructing an instance of [Python]. * * The following fields are required: * ```java - * .input() - * .model() * .name() + * .source() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [ScoreModel]. */ + /** A builder for [Python]. */ 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 source: JsonField? = null + private var type: JsonValue = JsonValue.from("python") + private var imageTag: JsonField = JsonMissing.of() 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() + 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 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)) @@ -4372,13 +4399,25 @@ private constructor( */ 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("score_model") + * JsonValue.from("python") * ``` * * This method is primarily for setting the field to an undocumented or not yet @@ -4386,50 +4425,31 @@ private constructor( */ fun type(type: JsonValue) = apply { this.type = type } - /** The threshold for the score. */ - fun passThreshold(passThreshold: Double) = - passThreshold(JsonField.of(passThreshold)) + /** The image tag to use for the python script. */ + fun imageTag(imageTag: String) = imageTag(JsonField.of(imageTag)) /** - * Sets [Builder.passThreshold] to an arbitrary JSON value. + * Sets [Builder.imageTag] to an arbitrary JSON value. * - * You should usually call [Builder.passThreshold] with a well-typed [Double] 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 passThreshold(passThreshold: JsonField) = apply { - this.passThreshold = passThreshold - } + fun imageTag(imageTag: JsonField) = apply { this.imageTag = imageTag } - /** The range of the score. Defaults to `[0, 1]`. */ - fun range(range: List) = range(JsonField.of(range)) + /** The threshold for the score. */ + fun passThreshold(passThreshold: Double) = + passThreshold(JsonField.of(passThreshold)) /** - * Sets [Builder.range] to an arbitrary JSON value. + * Sets [Builder.passThreshold] to an arbitrary JSON value. * - * You should usually call [Builder.range] with a well-typed `List` 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 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 passThreshold(passThreshold: JsonField) = apply { + this.passThreshold = passThreshold } fun additionalProperties(additionalProperties: Map) = apply { @@ -4455,49 +4475,45 @@ private constructor( } /** - * Returns an immutable instance of [ScoreModel]. + * Returns an immutable instance of [Python]. * * Further updates to this [Builder] will not mutate the returned instance. * * The following fields are required: * ```java - * .input() - * .model() * .name() + * .source() * ``` * * @throws IllegalStateException if any required field is unset. */ - fun build(): ScoreModel = - ScoreModel( - checkRequired("input", input).map { it.toImmutable() }, - checkRequired("model", model), + fun build(): Python = + Python( checkRequired("name", name), + checkRequired("source", source), type, + imageTag, passThreshold, - (range ?: JsonMissing.of()).map { it.toImmutable() }, - samplingParams, additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): ScoreModel = apply { + fun validate(): Python = apply { if (validated) { return@apply } - input().forEach { it.validate() } - model() name() + source() _type().let { - if (it != JsonValue.from("score_model")) { + if (it != JsonValue.from("python")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") } } + imageTag() passThreshold() - range() validated = true } @@ -4517,1007 +4533,442 @@ private constructor( */ @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") + (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) - /** - * 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 equals(other: Any?): Boolean { + if (this === other) { + return true + } - /** - * 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") + 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 */ + } - /** - * 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 + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, 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 + override fun hashCode(): Int = hashCode - /** - * 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 toString() = + "Python{name=$name, source=$source, type=$type, imageTag=$imageTag, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" + } - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } + /** 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, + ) { - @JsonAnyGetter + @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 - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) + 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() - fun toBuilder() = Builder().from(this) + /** + * 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") - /** - * Returns a mutable builder for constructing an instance of [Input]. - * - * The following fields are required: - * ```java - * .content() - * .role() - * ``` - */ - @JvmStatic fun builder() = Builder() - } + /** + * 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 builder for [Input]. */ - class Builder internal constructor() { + /** + * 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 - private var content: JsonField? = null - private var role: JsonField? = null - private var type: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() + /** + * 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") - @JvmSynthetic - internal fun from(input: Input) = apply { - content = input.content - role = input.role - type = input.type - additionalProperties = input.additionalProperties.toMutableMap() - } + /** The sampling parameters for the model. */ + @JsonProperty("sampling_params") + @ExcludeMissing + fun _samplingParams(): JsonValue = samplingParams - /** 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 } + /** + * 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") - /** Alias for calling [content] with `Content.ofTextInput(textInput)`. */ - fun content(textInput: String) = content(Content.ofTextInput(textInput)) + /** + * 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 - /** - * Alias for calling [content] with - * `Content.ofResponseInputText(responseInputText)`. - */ - fun content(responseInputText: ResponseInputText) = - content(Content.ofResponseInputText(responseInputText)) + /** + * 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 - /** Alias for calling [content] with `Content.ofOutputText(outputText)`. */ - fun content(outputText: Content.OutputText) = - content(Content.ofOutputText(outputText)) + /** + * 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 - /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. - */ - fun role(role: Role) = role(JsonField.of(role)) + /** + * 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 - /** - * 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 } + /** + * 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 - /** The type of the message input. Always `message`. */ - fun type(type: Type) = type(JsonField.of(type)) + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } - /** - * 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 } + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } + fun toBuilder() = Builder().from(this) - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } + companion object { - fun putAllAdditionalProperties(additionalProperties: Map) = - apply { - this.additionalProperties.putAll(additionalProperties) - } + /** + * Returns a mutable builder for constructing an instance of [ScoreModel]. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + */ + @JvmStatic fun builder() = Builder() + } - fun removeAdditionalProperty(key: String) = apply { - additionalProperties.remove(key) - } + /** A builder for [ScoreModel]. */ + class Builder internal constructor() { - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + 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() - /** - * 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(), - ) + @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() } - private var validated: Boolean = false - - fun validate(): Input = apply { - if (validated) { - return@apply - } + /** The input text. This may include template strings. */ + fun input(input: List) = input(JsonField.of(input)) - content().validate() - role().validate() - type().ifPresent { it.validate() } - validated = true + /** + * 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() } } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: OpenAIInvalidDataException) { - false - } - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. + * Adds a single [ScoreModelGrader.Input] to [Builder.input]. * - * Used for best match union deserialization. + * @throws IllegalStateException if the field was previously set to a non-list. */ - @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 addInput(input: ScoreModelGrader.Input) = apply { + this.input = + (this.input ?: JsonField.of(mutableListOf())).also { + checkKnown("input", it).add(input) + } + } - /** A text input to the model. */ - fun textInput(): Optional = Optional.ofNullable(textInput) + /** The model to use for the evaluation. */ + fun model(model: String) = model(JsonField.of(model)) - /** A text input to the model. */ - fun responseInputText(): Optional = - Optional.ofNullable(responseInputText) + /** + * 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 } - /** 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 + /** + * 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 } - fun isOutputText(): Boolean = outputText != null + /** The range of the score. Defaults to `[0, 1]`. */ + fun range(range: List) = range(JsonField.of(range)) - /** A text input to the model. */ - fun asTextInput(): String = textInput.getOrThrow("textInput") + /** + * 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() } + } - /** A text input to the model. */ - fun asResponseInputText(): ResponseInputText = - responseInputText.getOrThrow("responseInputText") + /** + * 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) + } + } - /** A text output from the model. */ - fun asOutputText(): OutputText = outputText.getOrThrow("outputText") + /** The sampling parameters for the model. */ + fun samplingParams(samplingParams: JsonValue) = apply { + this.samplingParams = samplingParams + } - fun _json(): Optional = Optional.ofNullable(_json) + /** The threshold for the score. */ + fun passThreshold(passThreshold: Double) = + passThreshold(JsonField.of(passThreshold)) - 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) - } + /** + * 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(): Content = apply { - if (validated) { - return@apply - } + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - accept( - object : Visitor { - override fun visitTextInput(textInput: String) {} + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } - override fun visitResponseInputText( - responseInputText: ResponseInputText - ) { - responseInputText.validate() - } + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - 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}" - } - } + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } /** - * The role of the message input. One of `user`, `assistant`, `system`, or - * `developer`. + * 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. */ - 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 */ - } + 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(), + ) + } - override fun hashCode() = value.hashCode() + private var validated: Boolean = false - override fun toString() = value.toString() + fun validate(): ScoreModel = apply { + if (validated) { + return@apply } - /** 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 */ + input().forEach { it.validate() } + model() + name() + _type().let { + if (it != JsonValue.from("score_model")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") } - - override fun hashCode() = value.hashCode() - - override fun toString() = value.toString() } + range() + passThreshold() + validated = true + } - 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 */ + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false } - /* 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}" - } + /** + * 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 40073b39a..0982511fe 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 @@ -388,34 +391,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() @@ -860,11 +872,11 @@ private constructor( @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 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, ) { @@ -872,65 +884,71 @@ private constructor( * A LabelModelGrader object which uses a model to assign labels to each item in the * evaluation. */ - fun labelModel(): Optional = Optional.ofNullable(labelModel) + fun labelModelGrader(): Optional = Optional.ofNullable(labelModelGrader) /** * A StringCheckGrader object that performs a string comparison between input and reference * using a specified operation. */ - fun stringCheck(): Optional = Optional.ofNullable(stringCheck) + fun stringCheckGrader(): Optional = + Optional.ofNullable(stringCheckGrader) /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun textSimilarity(): Optional = - Optional.ofNullable(textSimilarity) + fun evalGraderTextSimilarity(): Optional = + Optional.ofNullable(evalGraderTextSimilarity) /** A PythonGrader object that runs a python script on the input. */ - fun python(): Optional = Optional.ofNullable(python) + fun evalGraderPython(): Optional = Optional.ofNullable(evalGraderPython) /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun scoreModel(): Optional = Optional.ofNullable(scoreModel) + fun evalGraderScoreModel(): Optional = + Optional.ofNullable(evalGraderScoreModel) - fun isLabelModel(): Boolean = labelModel != null + fun isLabelModelGrader(): Boolean = labelModelGrader != null - fun isStringCheck(): Boolean = stringCheck != null + fun isStringCheckGrader(): Boolean = stringCheckGrader != null - fun isTextSimilarity(): Boolean = textSimilarity != null + fun isEvalGraderTextSimilarity(): Boolean = evalGraderTextSimilarity != null - fun isPython(): Boolean = python != null + fun isEvalGraderPython(): Boolean = evalGraderPython != null - fun isScoreModel(): Boolean = scoreModel != null + fun isEvalGraderScoreModel(): Boolean = evalGraderScoreModel != null /** * A LabelModelGrader object which uses a model to assign labels to each item in the * evaluation. */ - fun asLabelModel(): EvalLabelModelGrader = labelModel.getOrThrow("labelModel") + fun asLabelModelGrader(): LabelModelGrader = labelModelGrader.getOrThrow("labelModelGrader") /** * A StringCheckGrader object that performs a string comparison between input and reference * using a specified operation. */ - fun asStringCheck(): EvalStringCheckGrader = stringCheck.getOrThrow("stringCheck") + fun asStringCheckGrader(): StringCheckGrader = + stringCheckGrader.getOrThrow("stringCheckGrader") /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun asTextSimilarity(): EvalTextSimilarityGrader = - textSimilarity.getOrThrow("textSimilarity") + fun asEvalGraderTextSimilarity(): EvalGraderTextSimilarity = + evalGraderTextSimilarity.getOrThrow("evalGraderTextSimilarity") /** A PythonGrader object that runs a python script on the input. */ - fun asPython(): Python = python.getOrThrow("python") + fun asEvalGraderPython(): EvalGraderPython = evalGraderPython.getOrThrow("evalGraderPython") /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun asScoreModel(): ScoreModel = scoreModel.getOrThrow("scoreModel") + fun asEvalGraderScoreModel(): EvalGraderScoreModel = + evalGraderScoreModel.getOrThrow("evalGraderScoreModel") fun _json(): Optional = Optional.ofNullable(_json) 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) + 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) } @@ -943,24 +961,28 @@ private constructor( accept( object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) { - labelModel.validate() + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) { + labelModelGrader.validate() } - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) { - stringCheck.validate() + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) { + stringCheckGrader.validate() } - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) { - textSimilarity.validate() + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) { + evalGraderTextSimilarity.validate() } - override fun visitPython(python: Python) { - python.validate() + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) { + evalGraderPython.validate() } - override fun visitScoreModel(scoreModel: ScoreModel) { - scoreModel.validate() + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) { + evalGraderScoreModel.validate() } } ) @@ -985,18 +1007,22 @@ private constructor( internal fun validity(): Int = accept( object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) = - labelModel.validity() + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) = + labelModelGrader.validity() - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) = - stringCheck.validity() + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) = + stringCheckGrader.validity() - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - textSimilarity.validity() + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) = evalGraderTextSimilarity.validity() - override fun visitPython(python: Python) = python.validity() + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) = + evalGraderPython.validity() - override fun visitScoreModel(scoreModel: ScoreModel) = scoreModel.validity() + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) = evalGraderScoreModel.validity() override fun unknown(json: JsonValue?) = 0 } @@ -1007,18 +1033,21 @@ private constructor( 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 */ + 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(labelModel, stringCheck, textSimilarity, python, scoreModel) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(labelModelGrader, stringCheckGrader, evalGraderTextSimilarity, evalGraderPython, evalGraderScoreModel) /* 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}" + 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") } @@ -1030,28 +1059,31 @@ private constructor( * evaluation. */ @JvmStatic - fun ofLabelModel(labelModel: EvalLabelModelGrader) = - TestingCriterion(labelModel = labelModel) + 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 ofStringCheck(stringCheck: EvalStringCheckGrader) = - TestingCriterion(stringCheck = stringCheck) + fun ofStringCheckGrader(stringCheckGrader: StringCheckGrader) = + TestingCriterion(stringCheckGrader = stringCheckGrader) /** A TextSimilarityGrader object which grades text based on similarity metrics. */ @JvmStatic - fun ofTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - TestingCriterion(textSimilarity = textSimilarity) + fun ofEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity) = + TestingCriterion(evalGraderTextSimilarity = evalGraderTextSimilarity) /** A PythonGrader object that runs a python script on the input. */ - @JvmStatic fun ofPython(python: Python) = TestingCriterion(python = python) + @JvmStatic + fun ofEvalGraderPython(evalGraderPython: EvalGraderPython) = + TestingCriterion(evalGraderPython = evalGraderPython) /** A ScoreModelGrader object that uses a model to assign a score to the input. */ @JvmStatic - fun ofScoreModel(scoreModel: ScoreModel) = TestingCriterion(scoreModel = scoreModel) + fun ofEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel) = + TestingCriterion(evalGraderScoreModel = evalGraderScoreModel) } /** @@ -1064,22 +1096,22 @@ private constructor( * A LabelModelGrader object which uses a model to assign labels to each item in the * evaluation. */ - fun visitLabelModel(labelModel: EvalLabelModelGrader): T + fun visitLabelModelGrader(labelModelGrader: LabelModelGrader): T /** * A StringCheckGrader object that performs a string comparison between input and * reference using a specified operation. */ - fun visitStringCheck(stringCheck: EvalStringCheckGrader): T + fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader): T /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader): T + fun visitEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity): T /** A PythonGrader object that runs a python script on the input. */ - fun visitPython(python: Python): T + fun visitEvalGraderPython(evalGraderPython: EvalGraderPython): T /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun visitScoreModel(scoreModel: ScoreModel): T + fun visitEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel): T /** * Maps an unknown variant of [TestingCriterion] to a value of type [T]. @@ -1100,37 +1132,38 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): TestingCriterion { val json = JsonValue.fromJsonNode(node) - val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() - 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) - } + 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() } - - return TestingCriterion(_json = json) } } @@ -1142,42 +1175,77 @@ private constructor( 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.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 PythonGrader object that runs a python script on the input. */ - class Python + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + class EvalGraderTextSimilarity private constructor( + private val evaluationMetric: JsonField, + private val input: JsonField, private val name: JsonField, - private val source: JsonField, + private val reference: JsonField, private val type: JsonValue, - private val imageTag: JsonField, private val passThreshold: JsonField, private val additionalProperties: MutableMap, ) { @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("source") + @JsonProperty("reference") @ExcludeMissing - source: JsonField = JsonMissing.of(), + reference: 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()) + ) : 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 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") + + /** + * 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") /** * The name of the grader. @@ -1189,20 +1257,20 @@ private constructor( fun name(): String = name.getRequired("name") /** - * The source code of the python script. + * 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 source(): String = source.getRequired("source") + fun reference(): String = reference.getRequired("reference") /** - * The object type, which is always `python`. + * The type of grader. * * Expected to always return the following: * ```java - * JsonValue.from("python") + * JsonValue.from("text_similarity") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1211,42 +1279,48 @@ private constructor( @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** - * The image tag to use for the python script. + * 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 imageTag(): Optional = imageTag.getOptional("image_tag") + fun passThreshold(): Double = passThreshold.getRequired("pass_threshold") /** - * The threshold for the score. + * 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 passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + @JsonProperty("evaluation_metric") + @ExcludeMissing + fun _evaluationMetric(): JsonField = + evaluationMetric /** - * Returns the raw JSON value of [name]. + * Returns the raw JSON value of [input]. * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JsonProperty("input") @ExcludeMissing fun _input(): JsonField = input /** - * Returns the raw JSON value of [source]. + * Returns the raw JSON value of [name]. * - * Unlike [source], 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("source") @ExcludeMissing fun _source(): JsonField = source + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name /** - * Returns the raw JSON value of [imageTag]. + * Returns the raw JSON value of [reference]. * - * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected * type. */ - @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag + @JsonProperty("reference") + @ExcludeMissing + fun _reference(): JsonField = reference /** * Returns the raw JSON value of [passThreshold]. @@ -1273,37 +1347,75 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [Python]. + * Returns a mutable builder for constructing an instance of + * [EvalGraderTextSimilarity]. * * The following fields are required: * ```java + * .evaluationMetric() + * .input() * .name() - * .source() + * .reference() + * .passThreshold() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [Python]. */ + /** A builder for [EvalGraderTextSimilarity]. */ class Builder internal constructor() { + private var evaluationMetric: JsonField? = + null + private var input: JsonField? = null 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 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(python: Python) = apply { - name = python.name - source = python.source - type = python.type - imageTag = python.imageTag - passThreshold = python.passThreshold - additionalProperties = python.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 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)) + + /** + * Sets [Builder.evaluationMetric] to an arbitrary JSON value. + * + * 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 evaluationMetric( + evaluationMetric: JsonField + ) = apply { this.evaluationMetric = evaluationMetric } + + /** The text being graded. */ + fun input(input: String) = input(JsonField.of(input)) + + /** + * 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 } + /** The name of the grader. */ fun name(name: String) = name(JsonField.of(name)) @@ -1316,17 +1428,17 @@ private constructor( */ fun name(name: JsonField) = apply { this.name = name } - /** The source code of the python script. */ - fun source(source: String) = source(JsonField.of(source)) + /** The text being graded against. */ + fun reference(reference: String) = reference(JsonField.of(reference)) /** - * Sets [Builder.source] to an arbitrary JSON value. + * Sets [Builder.reference] to an arbitrary JSON value. * - * You should usually call [Builder.source] with a well-typed [String] 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 source(source: JsonField) = apply { this.source = source } + fun reference(reference: JsonField) = apply { this.reference = reference } /** * Sets the field to an arbitrary JSON value. @@ -1334,7 +1446,7 @@ private constructor( * It is usually unnecessary to call this method because the field defaults to the * following: * ```java - * JsonValue.from("python") + * JsonValue.from("text_similarity") * ``` * * This method is primarily for setting the field to an undocumented or not yet @@ -1342,18 +1454,6 @@ private constructor( */ 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)) @@ -1392,44 +1492,49 @@ private constructor( } /** - * Returns an immutable instance of [Python]. + * 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() * .name() - * .source() + * .reference() + * .passThreshold() * ``` * * @throws IllegalStateException if any required field is unset. */ - fun build(): Python = - Python( + fun build(): EvalGraderTextSimilarity = + EvalGraderTextSimilarity( + checkRequired("evaluationMetric", evaluationMetric), + checkRequired("input", input), checkRequired("name", name), - checkRequired("source", source), + checkRequired("reference", reference), type, - imageTag, - passThreshold, + checkRequired("passThreshold", passThreshold), additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): Python = apply { + fun validate(): EvalGraderTextSimilarity = apply { if (validated) { return@apply } + evaluationMetric().validate() + input() name() - source() + reference() _type().let { - if (it != JsonValue.from("python")) { + if (it != JsonValue.from("text_similarity")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - imageTag() passThreshold() validated = true } @@ -1450,10 +1555,11 @@ private constructor( */ @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) + + (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 equals(other: Any?): Boolean { @@ -1461,84 +1567,77 @@ private constructor( 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 EvalGraderTextSimilarity && evaluationMetric == other.evaluationMetric && input == other.input && name == other.name && reference == other.reference && type == other.type && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(evaluationMetric, input, name, reference, type, 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}" + "EvalGraderTextSimilarity{evaluationMetric=$evaluationMetric, input=$input, name=$name, reference=$reference, type=$type, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - class ScoreModel + /** A PythonGrader object that runs a python script on the input. */ + class EvalGraderPython private constructor( - private val input: JsonField>, - private val model: JsonField, private val name: JsonField, + private val source: JsonField, private val type: JsonValue, + private val imageTag: JsonField, 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("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(), - @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(name, source, type, imageTag, passThreshold, 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") + fun toPythonGrader(): PythonGrader = + PythonGrader.builder() + .name(name) + .source(source) + .type(type) + .imageTag(imageTag) + .build() /** - * The model to use for the evaluation. + * 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 model(): String = model.getRequired("model") + fun name(): String = name.getRequired("name") /** - * The name of the grader. + * 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 name(): String = name.getRequired("name") + fun source(): String = source.getRequired("source") /** - * The object type, which is always `score_model`. + * The object type, which is always `python`. * * Expected to always return the following: * ```java - * JsonValue.from("score_model") + * JsonValue.from("python") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1547,46 +1646,42 @@ private constructor( @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** - * The threshold for the score. + * 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 passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + fun imageTag(): Optional = imageTag.getOptional("image_tag") /** - * The range of the score. Defaults to `[0, 1]`. + * 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 range(): Optional> = range.getOptional("range") - - /** The sampling parameters for the model. */ - @JsonProperty("sampling_params") - @ExcludeMissing - fun _samplingParams(): JsonValue = samplingParams + fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") /** - * Returns the raw JSON value of [input]. + * Returns the raw JSON value of [name]. * - * Unlike [input], 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("input") @ExcludeMissing fun _input(): JsonField> = input + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name /** - * Returns the raw JSON value of [model]. + * Returns the raw JSON value of [source]. * - * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source /** - * Returns the raw JSON value of [name]. + * Returns the raw JSON value of [imageTag]. * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected + * type. */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag /** * Returns the raw JSON value of [passThreshold]. @@ -1598,13 +1693,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,80 +1708,37 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [ScoreModel]. + * Returns a mutable builder for constructing an instance of [EvalGraderPython]. * * The following fields are required: * ```java - * .input() - * .model() * .name() + * .source() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [ScoreModel]. */ + /** A builder for [EvalGraderPython]. */ 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 source: JsonField? = null + private var type: JsonValue = JsonValue.from("python") + private var imageTag: JsonField = JsonMissing.of() 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) - } + 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() } - /** 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)) @@ -1706,8 +1751,410 @@ private constructor( */ fun name(name: JsonField) = apply { this.name = name } + /** The source code of the python script. */ + fun source(source: String) = source(JsonField.of(source)) + /** - * Sets the field to an arbitrary JSON value. + * 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 [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(), + ) + } + + private var validated: Boolean = false + + fun validate(): EvalGraderPython = 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 EvalGraderPython && 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() = + "EvalGraderPython{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 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, + ) { + + @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() + + /** + * 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 + + /** + * 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 [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 + + /** + * 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 [EvalGraderScoreModel]. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [EvalGraderScoreModel]. */ + 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 passThreshold: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @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() + } + + /** 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 [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 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: @@ -1720,21 +2167,6 @@ private constructor( */ 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)) @@ -1766,6 +2198,21 @@ private constructor( this.samplingParams = samplingParams } + /** 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) @@ -1789,7 +2236,7 @@ private constructor( } /** - * Returns an immutable instance of [ScoreModel]. + * Returns an immutable instance of [EvalGraderScoreModel]. * * Further updates to this [Builder] will not mutate the returned instance. * @@ -1802,22 +2249,22 @@ private constructor( * * @throws IllegalStateException if any required field is unset. */ - fun build(): ScoreModel = - ScoreModel( + fun build(): EvalGraderScoreModel = + EvalGraderScoreModel( checkRequired("input", input).map { it.toImmutable() }, checkRequired("model", model), checkRequired("name", name), type, - passThreshold, (range ?: JsonMissing.of()).map { it.toImmutable() }, samplingParams, + passThreshold, additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): ScoreModel = apply { + fun validate(): EvalGraderScoreModel = apply { if (validated) { return@apply } @@ -1830,8 +2277,8 @@ private constructor( throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - passThreshold() range() + passThreshold() validated = true } @@ -1855,1003 +2302,25 @@ private constructor( (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 { - - /** - * 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}" - } + (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/EvalListResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalListResponse.kt index 2d3a7b7d1..e10091075 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 @@ -388,34 +391,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() @@ -860,11 +872,11 @@ private constructor( @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 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, ) { @@ -872,65 +884,71 @@ private constructor( * A LabelModelGrader object which uses a model to assign labels to each item in the * evaluation. */ - fun labelModel(): Optional = Optional.ofNullable(labelModel) + fun labelModelGrader(): Optional = Optional.ofNullable(labelModelGrader) /** * A StringCheckGrader object that performs a string comparison between input and reference * using a specified operation. */ - fun stringCheck(): Optional = Optional.ofNullable(stringCheck) + fun stringCheckGrader(): Optional = + Optional.ofNullable(stringCheckGrader) /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun textSimilarity(): Optional = - Optional.ofNullable(textSimilarity) + fun evalGraderTextSimilarity(): Optional = + Optional.ofNullable(evalGraderTextSimilarity) /** A PythonGrader object that runs a python script on the input. */ - fun python(): Optional = Optional.ofNullable(python) + fun evalGraderPython(): Optional = Optional.ofNullable(evalGraderPython) /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun scoreModel(): Optional = Optional.ofNullable(scoreModel) + fun evalGraderScoreModel(): Optional = + Optional.ofNullable(evalGraderScoreModel) - fun isLabelModel(): Boolean = labelModel != null + fun isLabelModelGrader(): Boolean = labelModelGrader != null - fun isStringCheck(): Boolean = stringCheck != null + fun isStringCheckGrader(): Boolean = stringCheckGrader != null - fun isTextSimilarity(): Boolean = textSimilarity != null + fun isEvalGraderTextSimilarity(): Boolean = evalGraderTextSimilarity != null - fun isPython(): Boolean = python != null + fun isEvalGraderPython(): Boolean = evalGraderPython != null - fun isScoreModel(): Boolean = scoreModel != null + fun isEvalGraderScoreModel(): Boolean = evalGraderScoreModel != null /** * A LabelModelGrader object which uses a model to assign labels to each item in the * evaluation. */ - fun asLabelModel(): EvalLabelModelGrader = labelModel.getOrThrow("labelModel") + fun asLabelModelGrader(): LabelModelGrader = labelModelGrader.getOrThrow("labelModelGrader") /** * A StringCheckGrader object that performs a string comparison between input and reference * using a specified operation. */ - fun asStringCheck(): EvalStringCheckGrader = stringCheck.getOrThrow("stringCheck") + fun asStringCheckGrader(): StringCheckGrader = + stringCheckGrader.getOrThrow("stringCheckGrader") /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun asTextSimilarity(): EvalTextSimilarityGrader = - textSimilarity.getOrThrow("textSimilarity") + fun asEvalGraderTextSimilarity(): EvalGraderTextSimilarity = + evalGraderTextSimilarity.getOrThrow("evalGraderTextSimilarity") /** A PythonGrader object that runs a python script on the input. */ - fun asPython(): Python = python.getOrThrow("python") + fun asEvalGraderPython(): EvalGraderPython = evalGraderPython.getOrThrow("evalGraderPython") /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun asScoreModel(): ScoreModel = scoreModel.getOrThrow("scoreModel") + fun asEvalGraderScoreModel(): EvalGraderScoreModel = + evalGraderScoreModel.getOrThrow("evalGraderScoreModel") fun _json(): Optional = Optional.ofNullable(_json) 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) + 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) } @@ -943,24 +961,28 @@ private constructor( accept( object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) { - labelModel.validate() + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) { + labelModelGrader.validate() } - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) { - stringCheck.validate() + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) { + stringCheckGrader.validate() } - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) { - textSimilarity.validate() + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) { + evalGraderTextSimilarity.validate() } - override fun visitPython(python: Python) { - python.validate() + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) { + evalGraderPython.validate() } - override fun visitScoreModel(scoreModel: ScoreModel) { - scoreModel.validate() + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) { + evalGraderScoreModel.validate() } } ) @@ -985,18 +1007,22 @@ private constructor( internal fun validity(): Int = accept( object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) = - labelModel.validity() + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) = + labelModelGrader.validity() - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) = - stringCheck.validity() + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) = + stringCheckGrader.validity() - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - textSimilarity.validity() + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) = evalGraderTextSimilarity.validity() - override fun visitPython(python: Python) = python.validity() + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) = + evalGraderPython.validity() - override fun visitScoreModel(scoreModel: ScoreModel) = scoreModel.validity() + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) = evalGraderScoreModel.validity() override fun unknown(json: JsonValue?) = 0 } @@ -1007,18 +1033,21 @@ private constructor( 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 */ + 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(labelModel, stringCheck, textSimilarity, python, scoreModel) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(labelModelGrader, stringCheckGrader, evalGraderTextSimilarity, evalGraderPython, evalGraderScoreModel) /* 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}" + 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") } @@ -1030,28 +1059,31 @@ private constructor( * evaluation. */ @JvmStatic - fun ofLabelModel(labelModel: EvalLabelModelGrader) = - TestingCriterion(labelModel = labelModel) + 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 ofStringCheck(stringCheck: EvalStringCheckGrader) = - TestingCriterion(stringCheck = stringCheck) + fun ofStringCheckGrader(stringCheckGrader: StringCheckGrader) = + TestingCriterion(stringCheckGrader = stringCheckGrader) /** A TextSimilarityGrader object which grades text based on similarity metrics. */ @JvmStatic - fun ofTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - TestingCriterion(textSimilarity = textSimilarity) + fun ofEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity) = + TestingCriterion(evalGraderTextSimilarity = evalGraderTextSimilarity) /** A PythonGrader object that runs a python script on the input. */ - @JvmStatic fun ofPython(python: Python) = TestingCriterion(python = python) + @JvmStatic + fun ofEvalGraderPython(evalGraderPython: EvalGraderPython) = + TestingCriterion(evalGraderPython = evalGraderPython) /** A ScoreModelGrader object that uses a model to assign a score to the input. */ @JvmStatic - fun ofScoreModel(scoreModel: ScoreModel) = TestingCriterion(scoreModel = scoreModel) + fun ofEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel) = + TestingCriterion(evalGraderScoreModel = evalGraderScoreModel) } /** @@ -1064,22 +1096,22 @@ private constructor( * A LabelModelGrader object which uses a model to assign labels to each item in the * evaluation. */ - fun visitLabelModel(labelModel: EvalLabelModelGrader): T + fun visitLabelModelGrader(labelModelGrader: LabelModelGrader): T /** * A StringCheckGrader object that performs a string comparison between input and * reference using a specified operation. */ - fun visitStringCheck(stringCheck: EvalStringCheckGrader): T + fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader): T /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader): T + fun visitEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity): T /** A PythonGrader object that runs a python script on the input. */ - fun visitPython(python: Python): T + fun visitEvalGraderPython(evalGraderPython: EvalGraderPython): T /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun visitScoreModel(scoreModel: ScoreModel): T + fun visitEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel): T /** * Maps an unknown variant of [TestingCriterion] to a value of type [T]. @@ -1100,37 +1132,38 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): TestingCriterion { val json = JsonValue.fromJsonNode(node) - val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() - 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) - } + 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() } - - return TestingCriterion(_json = json) } } @@ -1142,42 +1175,77 @@ private constructor( 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.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 PythonGrader object that runs a python script on the input. */ - class Python + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + class EvalGraderTextSimilarity private constructor( + private val evaluationMetric: JsonField, + private val input: JsonField, private val name: JsonField, - private val source: JsonField, + private val reference: JsonField, private val type: JsonValue, - private val imageTag: JsonField, private val passThreshold: JsonField, private val additionalProperties: MutableMap, ) { @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("source") + @JsonProperty("reference") @ExcludeMissing - source: JsonField = JsonMissing.of(), + reference: 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()) + ) : 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 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") + + /** + * 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") /** * The name of the grader. @@ -1189,20 +1257,20 @@ private constructor( fun name(): String = name.getRequired("name") /** - * The source code of the python script. + * 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 source(): String = source.getRequired("source") + fun reference(): String = reference.getRequired("reference") /** - * The object type, which is always `python`. + * The type of grader. * * Expected to always return the following: * ```java - * JsonValue.from("python") + * JsonValue.from("text_similarity") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1211,42 +1279,48 @@ private constructor( @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** - * The image tag to use for the python script. + * 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 imageTag(): Optional = imageTag.getOptional("image_tag") + fun passThreshold(): Double = passThreshold.getRequired("pass_threshold") /** - * The threshold for the score. + * 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 passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + @JsonProperty("evaluation_metric") + @ExcludeMissing + fun _evaluationMetric(): JsonField = + evaluationMetric /** - * Returns the raw JSON value of [name]. + * Returns the raw JSON value of [input]. * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JsonProperty("input") @ExcludeMissing fun _input(): JsonField = input /** - * Returns the raw JSON value of [source]. + * Returns the raw JSON value of [name]. * - * Unlike [source], 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("source") @ExcludeMissing fun _source(): JsonField = source + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name /** - * Returns the raw JSON value of [imageTag]. + * Returns the raw JSON value of [reference]. * - * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected * type. */ - @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag + @JsonProperty("reference") + @ExcludeMissing + fun _reference(): JsonField = reference /** * Returns the raw JSON value of [passThreshold]. @@ -1273,37 +1347,75 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [Python]. + * Returns a mutable builder for constructing an instance of + * [EvalGraderTextSimilarity]. * * The following fields are required: * ```java + * .evaluationMetric() + * .input() * .name() - * .source() + * .reference() + * .passThreshold() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [Python]. */ + /** A builder for [EvalGraderTextSimilarity]. */ class Builder internal constructor() { + private var evaluationMetric: JsonField? = + null + private var input: JsonField? = null 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 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(python: Python) = apply { - name = python.name - source = python.source - type = python.type - imageTag = python.imageTag - passThreshold = python.passThreshold - additionalProperties = python.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 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)) + + /** + * Sets [Builder.evaluationMetric] to an arbitrary JSON value. + * + * 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 evaluationMetric( + evaluationMetric: JsonField + ) = apply { this.evaluationMetric = evaluationMetric } + + /** The text being graded. */ + fun input(input: String) = input(JsonField.of(input)) + + /** + * 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 } + /** The name of the grader. */ fun name(name: String) = name(JsonField.of(name)) @@ -1316,17 +1428,17 @@ private constructor( */ fun name(name: JsonField) = apply { this.name = name } - /** The source code of the python script. */ - fun source(source: String) = source(JsonField.of(source)) + /** The text being graded against. */ + fun reference(reference: String) = reference(JsonField.of(reference)) /** - * Sets [Builder.source] to an arbitrary JSON value. + * Sets [Builder.reference] to an arbitrary JSON value. * - * You should usually call [Builder.source] with a well-typed [String] 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 source(source: JsonField) = apply { this.source = source } + fun reference(reference: JsonField) = apply { this.reference = reference } /** * Sets the field to an arbitrary JSON value. @@ -1334,7 +1446,7 @@ private constructor( * It is usually unnecessary to call this method because the field defaults to the * following: * ```java - * JsonValue.from("python") + * JsonValue.from("text_similarity") * ``` * * This method is primarily for setting the field to an undocumented or not yet @@ -1342,18 +1454,6 @@ private constructor( */ 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)) @@ -1392,44 +1492,49 @@ private constructor( } /** - * Returns an immutable instance of [Python]. + * 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() * .name() - * .source() + * .reference() + * .passThreshold() * ``` * * @throws IllegalStateException if any required field is unset. */ - fun build(): Python = - Python( + fun build(): EvalGraderTextSimilarity = + EvalGraderTextSimilarity( + checkRequired("evaluationMetric", evaluationMetric), + checkRequired("input", input), checkRequired("name", name), - checkRequired("source", source), + checkRequired("reference", reference), type, - imageTag, - passThreshold, + checkRequired("passThreshold", passThreshold), additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): Python = apply { + fun validate(): EvalGraderTextSimilarity = apply { if (validated) { return@apply } + evaluationMetric().validate() + input() name() - source() + reference() _type().let { - if (it != JsonValue.from("python")) { + if (it != JsonValue.from("text_similarity")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - imageTag() passThreshold() validated = true } @@ -1450,10 +1555,11 @@ private constructor( */ @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) + + (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 equals(other: Any?): Boolean { @@ -1461,84 +1567,77 @@ private constructor( 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 EvalGraderTextSimilarity && evaluationMetric == other.evaluationMetric && input == other.input && name == other.name && reference == other.reference && type == other.type && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(evaluationMetric, input, name, reference, type, 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}" + "EvalGraderTextSimilarity{evaluationMetric=$evaluationMetric, input=$input, name=$name, reference=$reference, type=$type, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - class ScoreModel + /** A PythonGrader object that runs a python script on the input. */ + class EvalGraderPython private constructor( - private val input: JsonField>, - private val model: JsonField, private val name: JsonField, + private val source: JsonField, private val type: JsonValue, + private val imageTag: JsonField, 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("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(), - @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(name, source, type, imageTag, passThreshold, 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") + fun toPythonGrader(): PythonGrader = + PythonGrader.builder() + .name(name) + .source(source) + .type(type) + .imageTag(imageTag) + .build() /** - * The model to use for the evaluation. + * 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 model(): String = model.getRequired("model") + fun name(): String = name.getRequired("name") /** - * The name of the grader. + * 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 name(): String = name.getRequired("name") + fun source(): String = source.getRequired("source") /** - * The object type, which is always `score_model`. + * The object type, which is always `python`. * * Expected to always return the following: * ```java - * JsonValue.from("score_model") + * JsonValue.from("python") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1547,46 +1646,42 @@ private constructor( @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** - * The threshold for the score. + * 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 passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + fun imageTag(): Optional = imageTag.getOptional("image_tag") /** - * The range of the score. Defaults to `[0, 1]`. + * 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 range(): Optional> = range.getOptional("range") - - /** The sampling parameters for the model. */ - @JsonProperty("sampling_params") - @ExcludeMissing - fun _samplingParams(): JsonValue = samplingParams + fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") /** - * Returns the raw JSON value of [input]. + * Returns the raw JSON value of [name]. * - * Unlike [input], 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("input") @ExcludeMissing fun _input(): JsonField> = input + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name /** - * Returns the raw JSON value of [model]. + * Returns the raw JSON value of [source]. * - * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source /** - * Returns the raw JSON value of [name]. + * Returns the raw JSON value of [imageTag]. * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected + * type. */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag /** * Returns the raw JSON value of [passThreshold]. @@ -1598,13 +1693,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,80 +1708,37 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [ScoreModel]. + * Returns a mutable builder for constructing an instance of [EvalGraderPython]. * * The following fields are required: * ```java - * .input() - * .model() * .name() + * .source() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [ScoreModel]. */ + /** A builder for [EvalGraderPython]. */ 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 source: JsonField? = null + private var type: JsonValue = JsonValue.from("python") + private var imageTag: JsonField = JsonMissing.of() 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) - } + 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() } - /** 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)) @@ -1706,8 +1751,410 @@ private constructor( */ fun name(name: JsonField) = apply { this.name = name } + /** The source code of the python script. */ + fun source(source: String) = source(JsonField.of(source)) + /** - * Sets the field to an arbitrary JSON value. + * 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 [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(), + ) + } + + private var validated: Boolean = false + + fun validate(): EvalGraderPython = 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 EvalGraderPython && 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() = + "EvalGraderPython{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 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, + ) { + + @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() + + /** + * 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 + + /** + * 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 [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 + + /** + * 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 [EvalGraderScoreModel]. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [EvalGraderScoreModel]. */ + 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 passThreshold: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @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() + } + + /** 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 [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 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: @@ -1720,21 +2167,6 @@ private constructor( */ 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)) @@ -1766,6 +2198,21 @@ private constructor( this.samplingParams = samplingParams } + /** 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) @@ -1789,7 +2236,7 @@ private constructor( } /** - * Returns an immutable instance of [ScoreModel]. + * Returns an immutable instance of [EvalGraderScoreModel]. * * Further updates to this [Builder] will not mutate the returned instance. * @@ -1802,22 +2249,22 @@ private constructor( * * @throws IllegalStateException if any required field is unset. */ - fun build(): ScoreModel = - ScoreModel( + fun build(): EvalGraderScoreModel = + EvalGraderScoreModel( checkRequired("input", input).map { it.toImmutable() }, checkRequired("model", model), checkRequired("name", name), type, - passThreshold, (range ?: JsonMissing.of()).map { it.toImmutable() }, samplingParams, + passThreshold, additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): ScoreModel = apply { + fun validate(): EvalGraderScoreModel = apply { if (validated) { return@apply } @@ -1830,8 +2277,8 @@ private constructor( throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - passThreshold() range() + passThreshold() validated = true } @@ -1855,1003 +2302,25 @@ private constructor( (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 { - - /** - * 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}" - } + (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/EvalRetrieveResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalRetrieveResponse.kt index f420c699d..56c27978d 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 @@ -388,34 +391,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() @@ -860,11 +872,11 @@ private constructor( @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 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, ) { @@ -872,65 +884,71 @@ private constructor( * A LabelModelGrader object which uses a model to assign labels to each item in the * evaluation. */ - fun labelModel(): Optional = Optional.ofNullable(labelModel) + fun labelModelGrader(): Optional = Optional.ofNullable(labelModelGrader) /** * A StringCheckGrader object that performs a string comparison between input and reference * using a specified operation. */ - fun stringCheck(): Optional = Optional.ofNullable(stringCheck) + fun stringCheckGrader(): Optional = + Optional.ofNullable(stringCheckGrader) /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun textSimilarity(): Optional = - Optional.ofNullable(textSimilarity) + fun evalGraderTextSimilarity(): Optional = + Optional.ofNullable(evalGraderTextSimilarity) /** A PythonGrader object that runs a python script on the input. */ - fun python(): Optional = Optional.ofNullable(python) + fun evalGraderPython(): Optional = Optional.ofNullable(evalGraderPython) /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun scoreModel(): Optional = Optional.ofNullable(scoreModel) + fun evalGraderScoreModel(): Optional = + Optional.ofNullable(evalGraderScoreModel) - fun isLabelModel(): Boolean = labelModel != null + fun isLabelModelGrader(): Boolean = labelModelGrader != null - fun isStringCheck(): Boolean = stringCheck != null + fun isStringCheckGrader(): Boolean = stringCheckGrader != null - fun isTextSimilarity(): Boolean = textSimilarity != null + fun isEvalGraderTextSimilarity(): Boolean = evalGraderTextSimilarity != null - fun isPython(): Boolean = python != null + fun isEvalGraderPython(): Boolean = evalGraderPython != null - fun isScoreModel(): Boolean = scoreModel != null + fun isEvalGraderScoreModel(): Boolean = evalGraderScoreModel != null /** * A LabelModelGrader object which uses a model to assign labels to each item in the * evaluation. */ - fun asLabelModel(): EvalLabelModelGrader = labelModel.getOrThrow("labelModel") + fun asLabelModelGrader(): LabelModelGrader = labelModelGrader.getOrThrow("labelModelGrader") /** * A StringCheckGrader object that performs a string comparison between input and reference * using a specified operation. */ - fun asStringCheck(): EvalStringCheckGrader = stringCheck.getOrThrow("stringCheck") + fun asStringCheckGrader(): StringCheckGrader = + stringCheckGrader.getOrThrow("stringCheckGrader") /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun asTextSimilarity(): EvalTextSimilarityGrader = - textSimilarity.getOrThrow("textSimilarity") + fun asEvalGraderTextSimilarity(): EvalGraderTextSimilarity = + evalGraderTextSimilarity.getOrThrow("evalGraderTextSimilarity") /** A PythonGrader object that runs a python script on the input. */ - fun asPython(): Python = python.getOrThrow("python") + fun asEvalGraderPython(): EvalGraderPython = evalGraderPython.getOrThrow("evalGraderPython") /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun asScoreModel(): ScoreModel = scoreModel.getOrThrow("scoreModel") + fun asEvalGraderScoreModel(): EvalGraderScoreModel = + evalGraderScoreModel.getOrThrow("evalGraderScoreModel") fun _json(): Optional = Optional.ofNullable(_json) 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) + 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) } @@ -943,24 +961,28 @@ private constructor( accept( object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) { - labelModel.validate() + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) { + labelModelGrader.validate() } - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) { - stringCheck.validate() + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) { + stringCheckGrader.validate() } - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) { - textSimilarity.validate() + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) { + evalGraderTextSimilarity.validate() } - override fun visitPython(python: Python) { - python.validate() + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) { + evalGraderPython.validate() } - override fun visitScoreModel(scoreModel: ScoreModel) { - scoreModel.validate() + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) { + evalGraderScoreModel.validate() } } ) @@ -985,18 +1007,22 @@ private constructor( internal fun validity(): Int = accept( object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) = - labelModel.validity() + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) = + labelModelGrader.validity() - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) = - stringCheck.validity() + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) = + stringCheckGrader.validity() - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - textSimilarity.validity() + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) = evalGraderTextSimilarity.validity() - override fun visitPython(python: Python) = python.validity() + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) = + evalGraderPython.validity() - override fun visitScoreModel(scoreModel: ScoreModel) = scoreModel.validity() + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) = evalGraderScoreModel.validity() override fun unknown(json: JsonValue?) = 0 } @@ -1007,18 +1033,21 @@ private constructor( 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 */ + 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(labelModel, stringCheck, textSimilarity, python, scoreModel) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(labelModelGrader, stringCheckGrader, evalGraderTextSimilarity, evalGraderPython, evalGraderScoreModel) /* 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}" + 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") } @@ -1030,28 +1059,31 @@ private constructor( * evaluation. */ @JvmStatic - fun ofLabelModel(labelModel: EvalLabelModelGrader) = - TestingCriterion(labelModel = labelModel) + 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 ofStringCheck(stringCheck: EvalStringCheckGrader) = - TestingCriterion(stringCheck = stringCheck) + fun ofStringCheckGrader(stringCheckGrader: StringCheckGrader) = + TestingCriterion(stringCheckGrader = stringCheckGrader) /** A TextSimilarityGrader object which grades text based on similarity metrics. */ @JvmStatic - fun ofTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - TestingCriterion(textSimilarity = textSimilarity) + fun ofEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity) = + TestingCriterion(evalGraderTextSimilarity = evalGraderTextSimilarity) /** A PythonGrader object that runs a python script on the input. */ - @JvmStatic fun ofPython(python: Python) = TestingCriterion(python = python) + @JvmStatic + fun ofEvalGraderPython(evalGraderPython: EvalGraderPython) = + TestingCriterion(evalGraderPython = evalGraderPython) /** A ScoreModelGrader object that uses a model to assign a score to the input. */ @JvmStatic - fun ofScoreModel(scoreModel: ScoreModel) = TestingCriterion(scoreModel = scoreModel) + fun ofEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel) = + TestingCriterion(evalGraderScoreModel = evalGraderScoreModel) } /** @@ -1064,22 +1096,22 @@ private constructor( * A LabelModelGrader object which uses a model to assign labels to each item in the * evaluation. */ - fun visitLabelModel(labelModel: EvalLabelModelGrader): T + fun visitLabelModelGrader(labelModelGrader: LabelModelGrader): T /** * A StringCheckGrader object that performs a string comparison between input and * reference using a specified operation. */ - fun visitStringCheck(stringCheck: EvalStringCheckGrader): T + fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader): T /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader): T + fun visitEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity): T /** A PythonGrader object that runs a python script on the input. */ - fun visitPython(python: Python): T + fun visitEvalGraderPython(evalGraderPython: EvalGraderPython): T /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun visitScoreModel(scoreModel: ScoreModel): T + fun visitEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel): T /** * Maps an unknown variant of [TestingCriterion] to a value of type [T]. @@ -1100,37 +1132,38 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): TestingCriterion { val json = JsonValue.fromJsonNode(node) - val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() - 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) - } + 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() } - - return TestingCriterion(_json = json) } } @@ -1142,42 +1175,77 @@ private constructor( 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.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 PythonGrader object that runs a python script on the input. */ - class Python + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + class EvalGraderTextSimilarity private constructor( + private val evaluationMetric: JsonField, + private val input: JsonField, private val name: JsonField, - private val source: JsonField, + private val reference: JsonField, private val type: JsonValue, - private val imageTag: JsonField, private val passThreshold: JsonField, private val additionalProperties: MutableMap, ) { @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("source") + @JsonProperty("reference") @ExcludeMissing - source: JsonField = JsonMissing.of(), + reference: 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()) + ) : 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 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") + + /** + * 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") /** * The name of the grader. @@ -1189,20 +1257,20 @@ private constructor( fun name(): String = name.getRequired("name") /** - * The source code of the python script. + * 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 source(): String = source.getRequired("source") + fun reference(): String = reference.getRequired("reference") /** - * The object type, which is always `python`. + * The type of grader. * * Expected to always return the following: * ```java - * JsonValue.from("python") + * JsonValue.from("text_similarity") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1211,42 +1279,48 @@ private constructor( @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** - * The image tag to use for the python script. + * 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 imageTag(): Optional = imageTag.getOptional("image_tag") + fun passThreshold(): Double = passThreshold.getRequired("pass_threshold") /** - * The threshold for the score. + * 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 passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + @JsonProperty("evaluation_metric") + @ExcludeMissing + fun _evaluationMetric(): JsonField = + evaluationMetric /** - * Returns the raw JSON value of [name]. + * Returns the raw JSON value of [input]. * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JsonProperty("input") @ExcludeMissing fun _input(): JsonField = input /** - * Returns the raw JSON value of [source]. + * Returns the raw JSON value of [name]. * - * Unlike [source], 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("source") @ExcludeMissing fun _source(): JsonField = source + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name /** - * Returns the raw JSON value of [imageTag]. + * Returns the raw JSON value of [reference]. * - * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected * type. */ - @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag + @JsonProperty("reference") + @ExcludeMissing + fun _reference(): JsonField = reference /** * Returns the raw JSON value of [passThreshold]. @@ -1273,37 +1347,75 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [Python]. + * Returns a mutable builder for constructing an instance of + * [EvalGraderTextSimilarity]. * * The following fields are required: * ```java + * .evaluationMetric() + * .input() * .name() - * .source() + * .reference() + * .passThreshold() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [Python]. */ + /** A builder for [EvalGraderTextSimilarity]. */ class Builder internal constructor() { + private var evaluationMetric: JsonField? = + null + private var input: JsonField? = null 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 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(python: Python) = apply { - name = python.name - source = python.source - type = python.type - imageTag = python.imageTag - passThreshold = python.passThreshold - additionalProperties = python.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 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)) + + /** + * Sets [Builder.evaluationMetric] to an arbitrary JSON value. + * + * 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 evaluationMetric( + evaluationMetric: JsonField + ) = apply { this.evaluationMetric = evaluationMetric } + + /** The text being graded. */ + fun input(input: String) = input(JsonField.of(input)) + + /** + * 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 } + /** The name of the grader. */ fun name(name: String) = name(JsonField.of(name)) @@ -1316,17 +1428,17 @@ private constructor( */ fun name(name: JsonField) = apply { this.name = name } - /** The source code of the python script. */ - fun source(source: String) = source(JsonField.of(source)) + /** The text being graded against. */ + fun reference(reference: String) = reference(JsonField.of(reference)) /** - * Sets [Builder.source] to an arbitrary JSON value. + * Sets [Builder.reference] to an arbitrary JSON value. * - * You should usually call [Builder.source] with a well-typed [String] 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 source(source: JsonField) = apply { this.source = source } + fun reference(reference: JsonField) = apply { this.reference = reference } /** * Sets the field to an arbitrary JSON value. @@ -1334,7 +1446,7 @@ private constructor( * It is usually unnecessary to call this method because the field defaults to the * following: * ```java - * JsonValue.from("python") + * JsonValue.from("text_similarity") * ``` * * This method is primarily for setting the field to an undocumented or not yet @@ -1342,18 +1454,6 @@ private constructor( */ 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)) @@ -1392,44 +1492,49 @@ private constructor( } /** - * Returns an immutable instance of [Python]. + * 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() * .name() - * .source() + * .reference() + * .passThreshold() * ``` * * @throws IllegalStateException if any required field is unset. */ - fun build(): Python = - Python( + fun build(): EvalGraderTextSimilarity = + EvalGraderTextSimilarity( + checkRequired("evaluationMetric", evaluationMetric), + checkRequired("input", input), checkRequired("name", name), - checkRequired("source", source), + checkRequired("reference", reference), type, - imageTag, - passThreshold, + checkRequired("passThreshold", passThreshold), additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): Python = apply { + fun validate(): EvalGraderTextSimilarity = apply { if (validated) { return@apply } + evaluationMetric().validate() + input() name() - source() + reference() _type().let { - if (it != JsonValue.from("python")) { + if (it != JsonValue.from("text_similarity")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - imageTag() passThreshold() validated = true } @@ -1450,10 +1555,11 @@ private constructor( */ @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) + + (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 equals(other: Any?): Boolean { @@ -1461,84 +1567,77 @@ private constructor( 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 EvalGraderTextSimilarity && evaluationMetric == other.evaluationMetric && input == other.input && name == other.name && reference == other.reference && type == other.type && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(evaluationMetric, input, name, reference, type, 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}" + "EvalGraderTextSimilarity{evaluationMetric=$evaluationMetric, input=$input, name=$name, reference=$reference, type=$type, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - class ScoreModel + /** A PythonGrader object that runs a python script on the input. */ + class EvalGraderPython private constructor( - private val input: JsonField>, - private val model: JsonField, private val name: JsonField, + private val source: JsonField, private val type: JsonValue, + private val imageTag: JsonField, 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("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(), - @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(name, source, type, imageTag, passThreshold, 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") + fun toPythonGrader(): PythonGrader = + PythonGrader.builder() + .name(name) + .source(source) + .type(type) + .imageTag(imageTag) + .build() /** - * The model to use for the evaluation. + * 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 model(): String = model.getRequired("model") + fun name(): String = name.getRequired("name") /** - * The name of the grader. + * 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 name(): String = name.getRequired("name") + fun source(): String = source.getRequired("source") /** - * The object type, which is always `score_model`. + * The object type, which is always `python`. * * Expected to always return the following: * ```java - * JsonValue.from("score_model") + * JsonValue.from("python") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1547,46 +1646,42 @@ private constructor( @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** - * The threshold for the score. + * 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 passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + fun imageTag(): Optional = imageTag.getOptional("image_tag") /** - * The range of the score. Defaults to `[0, 1]`. + * 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 range(): Optional> = range.getOptional("range") - - /** The sampling parameters for the model. */ - @JsonProperty("sampling_params") - @ExcludeMissing - fun _samplingParams(): JsonValue = samplingParams + fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") /** - * Returns the raw JSON value of [input]. + * Returns the raw JSON value of [name]. * - * Unlike [input], 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("input") @ExcludeMissing fun _input(): JsonField> = input + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name /** - * Returns the raw JSON value of [model]. + * Returns the raw JSON value of [source]. * - * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source /** - * Returns the raw JSON value of [name]. + * Returns the raw JSON value of [imageTag]. * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected + * type. */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag /** * Returns the raw JSON value of [passThreshold]. @@ -1598,13 +1693,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,80 +1708,37 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [ScoreModel]. + * Returns a mutable builder for constructing an instance of [EvalGraderPython]. * * The following fields are required: * ```java - * .input() - * .model() * .name() + * .source() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [ScoreModel]. */ + /** A builder for [EvalGraderPython]. */ 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 source: JsonField? = null + private var type: JsonValue = JsonValue.from("python") + private var imageTag: JsonField = JsonMissing.of() 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) - } + 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() } - /** 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)) @@ -1706,8 +1751,410 @@ private constructor( */ fun name(name: JsonField) = apply { this.name = name } + /** The source code of the python script. */ + fun source(source: String) = source(JsonField.of(source)) + /** - * Sets the field to an arbitrary JSON value. + * 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 [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(), + ) + } + + private var validated: Boolean = false + + fun validate(): EvalGraderPython = 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 EvalGraderPython && 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() = + "EvalGraderPython{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 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, + ) { + + @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() + + /** + * 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 + + /** + * 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 [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 + + /** + * 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 [EvalGraderScoreModel]. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [EvalGraderScoreModel]. */ + 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 passThreshold: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @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() + } + + /** 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 [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 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: @@ -1720,21 +2167,6 @@ private constructor( */ 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)) @@ -1766,6 +2198,21 @@ private constructor( this.samplingParams = samplingParams } + /** 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) @@ -1789,7 +2236,7 @@ private constructor( } /** - * Returns an immutable instance of [ScoreModel]. + * Returns an immutable instance of [EvalGraderScoreModel]. * * Further updates to this [Builder] will not mutate the returned instance. * @@ -1802,22 +2249,22 @@ private constructor( * * @throws IllegalStateException if any required field is unset. */ - fun build(): ScoreModel = - ScoreModel( + fun build(): EvalGraderScoreModel = + EvalGraderScoreModel( checkRequired("input", input).map { it.toImmutable() }, checkRequired("model", model), checkRequired("name", name), type, - passThreshold, (range ?: JsonMissing.of()).map { it.toImmutable() }, samplingParams, + passThreshold, additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): ScoreModel = apply { + fun validate(): EvalGraderScoreModel = apply { if (validated) { return@apply } @@ -1830,8 +2277,8 @@ private constructor( throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - passThreshold() range() + passThreshold() validated = true } @@ -1855,1003 +2302,25 @@ private constructor( (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 { - - /** - * 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}" - } + (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/EvalUpdateResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalUpdateResponse.kt index db7ad9a1d..5d05142a1 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 @@ -388,34 +391,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() @@ -860,11 +872,11 @@ private constructor( @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 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, ) { @@ -872,65 +884,71 @@ private constructor( * A LabelModelGrader object which uses a model to assign labels to each item in the * evaluation. */ - fun labelModel(): Optional = Optional.ofNullable(labelModel) + fun labelModelGrader(): Optional = Optional.ofNullable(labelModelGrader) /** * A StringCheckGrader object that performs a string comparison between input and reference * using a specified operation. */ - fun stringCheck(): Optional = Optional.ofNullable(stringCheck) + fun stringCheckGrader(): Optional = + Optional.ofNullable(stringCheckGrader) /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun textSimilarity(): Optional = - Optional.ofNullable(textSimilarity) + fun evalGraderTextSimilarity(): Optional = + Optional.ofNullable(evalGraderTextSimilarity) /** A PythonGrader object that runs a python script on the input. */ - fun python(): Optional = Optional.ofNullable(python) + fun evalGraderPython(): Optional = Optional.ofNullable(evalGraderPython) /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun scoreModel(): Optional = Optional.ofNullable(scoreModel) + fun evalGraderScoreModel(): Optional = + Optional.ofNullable(evalGraderScoreModel) - fun isLabelModel(): Boolean = labelModel != null + fun isLabelModelGrader(): Boolean = labelModelGrader != null - fun isStringCheck(): Boolean = stringCheck != null + fun isStringCheckGrader(): Boolean = stringCheckGrader != null - fun isTextSimilarity(): Boolean = textSimilarity != null + fun isEvalGraderTextSimilarity(): Boolean = evalGraderTextSimilarity != null - fun isPython(): Boolean = python != null + fun isEvalGraderPython(): Boolean = evalGraderPython != null - fun isScoreModel(): Boolean = scoreModel != null + fun isEvalGraderScoreModel(): Boolean = evalGraderScoreModel != null /** * A LabelModelGrader object which uses a model to assign labels to each item in the * evaluation. */ - fun asLabelModel(): EvalLabelModelGrader = labelModel.getOrThrow("labelModel") + fun asLabelModelGrader(): LabelModelGrader = labelModelGrader.getOrThrow("labelModelGrader") /** * A StringCheckGrader object that performs a string comparison between input and reference * using a specified operation. */ - fun asStringCheck(): EvalStringCheckGrader = stringCheck.getOrThrow("stringCheck") + fun asStringCheckGrader(): StringCheckGrader = + stringCheckGrader.getOrThrow("stringCheckGrader") /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun asTextSimilarity(): EvalTextSimilarityGrader = - textSimilarity.getOrThrow("textSimilarity") + fun asEvalGraderTextSimilarity(): EvalGraderTextSimilarity = + evalGraderTextSimilarity.getOrThrow("evalGraderTextSimilarity") /** A PythonGrader object that runs a python script on the input. */ - fun asPython(): Python = python.getOrThrow("python") + fun asEvalGraderPython(): EvalGraderPython = evalGraderPython.getOrThrow("evalGraderPython") /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun asScoreModel(): ScoreModel = scoreModel.getOrThrow("scoreModel") + fun asEvalGraderScoreModel(): EvalGraderScoreModel = + evalGraderScoreModel.getOrThrow("evalGraderScoreModel") fun _json(): Optional = Optional.ofNullable(_json) 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) + 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) } @@ -943,24 +961,28 @@ private constructor( accept( object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) { - labelModel.validate() + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) { + labelModelGrader.validate() } - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) { - stringCheck.validate() + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) { + stringCheckGrader.validate() } - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) { - textSimilarity.validate() + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) { + evalGraderTextSimilarity.validate() } - override fun visitPython(python: Python) { - python.validate() + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) { + evalGraderPython.validate() } - override fun visitScoreModel(scoreModel: ScoreModel) { - scoreModel.validate() + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) { + evalGraderScoreModel.validate() } } ) @@ -985,18 +1007,22 @@ private constructor( internal fun validity(): Int = accept( object : Visitor { - override fun visitLabelModel(labelModel: EvalLabelModelGrader) = - labelModel.validity() + override fun visitLabelModelGrader(labelModelGrader: LabelModelGrader) = + labelModelGrader.validity() - override fun visitStringCheck(stringCheck: EvalStringCheckGrader) = - stringCheck.validity() + override fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader) = + stringCheckGrader.validity() - override fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - textSimilarity.validity() + override fun visitEvalGraderTextSimilarity( + evalGraderTextSimilarity: EvalGraderTextSimilarity + ) = evalGraderTextSimilarity.validity() - override fun visitPython(python: Python) = python.validity() + override fun visitEvalGraderPython(evalGraderPython: EvalGraderPython) = + evalGraderPython.validity() - override fun visitScoreModel(scoreModel: ScoreModel) = scoreModel.validity() + override fun visitEvalGraderScoreModel( + evalGraderScoreModel: EvalGraderScoreModel + ) = evalGraderScoreModel.validity() override fun unknown(json: JsonValue?) = 0 } @@ -1007,18 +1033,21 @@ private constructor( 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 */ + 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(labelModel, stringCheck, textSimilarity, python, scoreModel) /* spotless:on */ + override fun hashCode(): Int = /* spotless:off */ Objects.hash(labelModelGrader, stringCheckGrader, evalGraderTextSimilarity, evalGraderPython, evalGraderScoreModel) /* 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}" + 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") } @@ -1030,28 +1059,31 @@ private constructor( * evaluation. */ @JvmStatic - fun ofLabelModel(labelModel: EvalLabelModelGrader) = - TestingCriterion(labelModel = labelModel) + 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 ofStringCheck(stringCheck: EvalStringCheckGrader) = - TestingCriterion(stringCheck = stringCheck) + fun ofStringCheckGrader(stringCheckGrader: StringCheckGrader) = + TestingCriterion(stringCheckGrader = stringCheckGrader) /** A TextSimilarityGrader object which grades text based on similarity metrics. */ @JvmStatic - fun ofTextSimilarity(textSimilarity: EvalTextSimilarityGrader) = - TestingCriterion(textSimilarity = textSimilarity) + fun ofEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity) = + TestingCriterion(evalGraderTextSimilarity = evalGraderTextSimilarity) /** A PythonGrader object that runs a python script on the input. */ - @JvmStatic fun ofPython(python: Python) = TestingCriterion(python = python) + @JvmStatic + fun ofEvalGraderPython(evalGraderPython: EvalGraderPython) = + TestingCriterion(evalGraderPython = evalGraderPython) /** A ScoreModelGrader object that uses a model to assign a score to the input. */ @JvmStatic - fun ofScoreModel(scoreModel: ScoreModel) = TestingCriterion(scoreModel = scoreModel) + fun ofEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel) = + TestingCriterion(evalGraderScoreModel = evalGraderScoreModel) } /** @@ -1064,22 +1096,22 @@ private constructor( * A LabelModelGrader object which uses a model to assign labels to each item in the * evaluation. */ - fun visitLabelModel(labelModel: EvalLabelModelGrader): T + fun visitLabelModelGrader(labelModelGrader: LabelModelGrader): T /** * A StringCheckGrader object that performs a string comparison between input and * reference using a specified operation. */ - fun visitStringCheck(stringCheck: EvalStringCheckGrader): T + fun visitStringCheckGrader(stringCheckGrader: StringCheckGrader): T /** A TextSimilarityGrader object which grades text based on similarity metrics. */ - fun visitTextSimilarity(textSimilarity: EvalTextSimilarityGrader): T + fun visitEvalGraderTextSimilarity(evalGraderTextSimilarity: EvalGraderTextSimilarity): T /** A PythonGrader object that runs a python script on the input. */ - fun visitPython(python: Python): T + fun visitEvalGraderPython(evalGraderPython: EvalGraderPython): T /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - fun visitScoreModel(scoreModel: ScoreModel): T + fun visitEvalGraderScoreModel(evalGraderScoreModel: EvalGraderScoreModel): T /** * Maps an unknown variant of [TestingCriterion] to a value of type [T]. @@ -1100,37 +1132,38 @@ private constructor( override fun ObjectCodec.deserialize(node: JsonNode): TestingCriterion { val json = JsonValue.fromJsonNode(node) - val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() - 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) - } + 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() } - - return TestingCriterion(_json = json) } } @@ -1142,42 +1175,77 @@ private constructor( 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.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 PythonGrader object that runs a python script on the input. */ - class Python + /** A TextSimilarityGrader object which grades text based on similarity metrics. */ + class EvalGraderTextSimilarity private constructor( + private val evaluationMetric: JsonField, + private val input: JsonField, private val name: JsonField, - private val source: JsonField, + private val reference: JsonField, private val type: JsonValue, - private val imageTag: JsonField, private val passThreshold: JsonField, private val additionalProperties: MutableMap, ) { @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("source") + @JsonProperty("reference") @ExcludeMissing - source: JsonField = JsonMissing.of(), + reference: 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()) + ) : 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 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") + + /** + * 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") /** * The name of the grader. @@ -1189,20 +1257,20 @@ private constructor( fun name(): String = name.getRequired("name") /** - * The source code of the python script. + * 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 source(): String = source.getRequired("source") + fun reference(): String = reference.getRequired("reference") /** - * The object type, which is always `python`. + * The type of grader. * * Expected to always return the following: * ```java - * JsonValue.from("python") + * JsonValue.from("text_similarity") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1211,42 +1279,48 @@ private constructor( @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** - * The image tag to use for the python script. + * 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 imageTag(): Optional = imageTag.getOptional("image_tag") + fun passThreshold(): Double = passThreshold.getRequired("pass_threshold") /** - * The threshold for the score. + * 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 passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + @JsonProperty("evaluation_metric") + @ExcludeMissing + fun _evaluationMetric(): JsonField = + evaluationMetric /** - * Returns the raw JSON value of [name]. + * Returns the raw JSON value of [input]. * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [input], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JsonProperty("input") @ExcludeMissing fun _input(): JsonField = input /** - * Returns the raw JSON value of [source]. + * Returns the raw JSON value of [name]. * - * Unlike [source], 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("source") @ExcludeMissing fun _source(): JsonField = source + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name /** - * Returns the raw JSON value of [imageTag]. + * Returns the raw JSON value of [reference]. * - * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected * type. */ - @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag + @JsonProperty("reference") + @ExcludeMissing + fun _reference(): JsonField = reference /** * Returns the raw JSON value of [passThreshold]. @@ -1273,37 +1347,75 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [Python]. + * Returns a mutable builder for constructing an instance of + * [EvalGraderTextSimilarity]. * * The following fields are required: * ```java + * .evaluationMetric() + * .input() * .name() - * .source() + * .reference() + * .passThreshold() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [Python]. */ + /** A builder for [EvalGraderTextSimilarity]. */ class Builder internal constructor() { + private var evaluationMetric: JsonField? = + null + private var input: JsonField? = null 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 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(python: Python) = apply { - name = python.name - source = python.source - type = python.type - imageTag = python.imageTag - passThreshold = python.passThreshold - additionalProperties = python.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 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)) + + /** + * Sets [Builder.evaluationMetric] to an arbitrary JSON value. + * + * 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 evaluationMetric( + evaluationMetric: JsonField + ) = apply { this.evaluationMetric = evaluationMetric } + + /** The text being graded. */ + fun input(input: String) = input(JsonField.of(input)) + + /** + * 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 } + /** The name of the grader. */ fun name(name: String) = name(JsonField.of(name)) @@ -1316,17 +1428,17 @@ private constructor( */ fun name(name: JsonField) = apply { this.name = name } - /** The source code of the python script. */ - fun source(source: String) = source(JsonField.of(source)) + /** The text being graded against. */ + fun reference(reference: String) = reference(JsonField.of(reference)) /** - * Sets [Builder.source] to an arbitrary JSON value. + * Sets [Builder.reference] to an arbitrary JSON value. * - * You should usually call [Builder.source] with a well-typed [String] 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 source(source: JsonField) = apply { this.source = source } + fun reference(reference: JsonField) = apply { this.reference = reference } /** * Sets the field to an arbitrary JSON value. @@ -1334,7 +1446,7 @@ private constructor( * It is usually unnecessary to call this method because the field defaults to the * following: * ```java - * JsonValue.from("python") + * JsonValue.from("text_similarity") * ``` * * This method is primarily for setting the field to an undocumented or not yet @@ -1342,18 +1454,6 @@ private constructor( */ 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)) @@ -1392,44 +1492,49 @@ private constructor( } /** - * Returns an immutable instance of [Python]. + * 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() * .name() - * .source() + * .reference() + * .passThreshold() * ``` * * @throws IllegalStateException if any required field is unset. */ - fun build(): Python = - Python( + fun build(): EvalGraderTextSimilarity = + EvalGraderTextSimilarity( + checkRequired("evaluationMetric", evaluationMetric), + checkRequired("input", input), checkRequired("name", name), - checkRequired("source", source), + checkRequired("reference", reference), type, - imageTag, - passThreshold, + checkRequired("passThreshold", passThreshold), additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): Python = apply { + fun validate(): EvalGraderTextSimilarity = apply { if (validated) { return@apply } + evaluationMetric().validate() + input() name() - source() + reference() _type().let { - if (it != JsonValue.from("python")) { + if (it != JsonValue.from("text_similarity")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - imageTag() passThreshold() validated = true } @@ -1450,10 +1555,11 @@ private constructor( */ @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) + + (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 equals(other: Any?): Boolean { @@ -1461,84 +1567,77 @@ private constructor( 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 EvalGraderTextSimilarity && evaluationMetric == other.evaluationMetric && input == other.input && name == other.name && reference == other.reference && type == other.type && passThreshold == other.passThreshold && additionalProperties == other.additionalProperties /* spotless:on */ } /* spotless:off */ - private val hashCode: Int by lazy { Objects.hash(name, source, type, imageTag, passThreshold, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(evaluationMetric, input, name, reference, type, 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}" + "EvalGraderTextSimilarity{evaluationMetric=$evaluationMetric, input=$input, name=$name, reference=$reference, type=$type, passThreshold=$passThreshold, additionalProperties=$additionalProperties}" } - /** A ScoreModelGrader object that uses a model to assign a score to the input. */ - class ScoreModel + /** A PythonGrader object that runs a python script on the input. */ + class EvalGraderPython private constructor( - private val input: JsonField>, - private val model: JsonField, private val name: JsonField, + private val source: JsonField, private val type: JsonValue, + private val imageTag: JsonField, 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("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(), - @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(name, source, type, imageTag, passThreshold, 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") + fun toPythonGrader(): PythonGrader = + PythonGrader.builder() + .name(name) + .source(source) + .type(type) + .imageTag(imageTag) + .build() /** - * The model to use for the evaluation. + * 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 model(): String = model.getRequired("model") + fun name(): String = name.getRequired("name") /** - * The name of the grader. + * 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 name(): String = name.getRequired("name") + fun source(): String = source.getRequired("source") /** - * The object type, which is always `score_model`. + * The object type, which is always `python`. * * Expected to always return the following: * ```java - * JsonValue.from("score_model") + * JsonValue.from("python") * ``` * * However, this method can be useful for debugging and logging (e.g. if the server @@ -1547,46 +1646,42 @@ private constructor( @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** - * The threshold for the score. + * 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 passThreshold(): Optional = passThreshold.getOptional("pass_threshold") + fun imageTag(): Optional = imageTag.getOptional("image_tag") /** - * The range of the score. Defaults to `[0, 1]`. + * 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 range(): Optional> = range.getOptional("range") - - /** The sampling parameters for the model. */ - @JsonProperty("sampling_params") - @ExcludeMissing - fun _samplingParams(): JsonValue = samplingParams + fun passThreshold(): Optional = passThreshold.getOptional("pass_threshold") /** - * Returns the raw JSON value of [input]. + * Returns the raw JSON value of [name]. * - * Unlike [input], 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("input") @ExcludeMissing fun _input(): JsonField> = input + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name /** - * Returns the raw JSON value of [model]. + * Returns the raw JSON value of [source]. * - * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source /** - * Returns the raw JSON value of [name]. + * Returns the raw JSON value of [imageTag]. * - * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [imageTag], this method doesn't throw if the JSON field has an unexpected + * type. */ - @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + @JsonProperty("image_tag") @ExcludeMissing fun _imageTag(): JsonField = imageTag /** * Returns the raw JSON value of [passThreshold]. @@ -1598,13 +1693,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,80 +1708,37 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [ScoreModel]. + * Returns a mutable builder for constructing an instance of [EvalGraderPython]. * * The following fields are required: * ```java - * .input() - * .model() * .name() + * .source() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [ScoreModel]. */ + /** A builder for [EvalGraderPython]. */ 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 source: JsonField? = null + private var type: JsonValue = JsonValue.from("python") + private var imageTag: JsonField = JsonMissing.of() 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) - } + 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() } - /** 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)) @@ -1706,8 +1751,410 @@ private constructor( */ fun name(name: JsonField) = apply { this.name = name } + /** The source code of the python script. */ + fun source(source: String) = source(JsonField.of(source)) + /** - * Sets the field to an arbitrary JSON value. + * 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 [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(), + ) + } + + private var validated: Boolean = false + + fun validate(): EvalGraderPython = 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 EvalGraderPython && 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() = + "EvalGraderPython{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 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, + ) { + + @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() + + /** + * 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 + + /** + * 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 [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 + + /** + * 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 [EvalGraderScoreModel]. + * + * The following fields are required: + * ```java + * .input() + * .model() + * .name() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [EvalGraderScoreModel]. */ + 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 passThreshold: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @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() + } + + /** 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 [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 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: @@ -1720,21 +2167,6 @@ private constructor( */ 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)) @@ -1766,6 +2198,21 @@ private constructor( this.samplingParams = samplingParams } + /** 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) @@ -1789,7 +2236,7 @@ private constructor( } /** - * Returns an immutable instance of [ScoreModel]. + * Returns an immutable instance of [EvalGraderScoreModel]. * * Further updates to this [Builder] will not mutate the returned instance. * @@ -1802,22 +2249,22 @@ private constructor( * * @throws IllegalStateException if any required field is unset. */ - fun build(): ScoreModel = - ScoreModel( + fun build(): EvalGraderScoreModel = + EvalGraderScoreModel( checkRequired("input", input).map { it.toImmutable() }, checkRequired("model", model), checkRequired("name", name), type, - passThreshold, (range ?: JsonMissing.of()).map { it.toImmutable() }, samplingParams, + passThreshold, additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): ScoreModel = apply { + fun validate(): EvalGraderScoreModel = apply { if (validated) { return@apply } @@ -1830,8 +2277,8 @@ private constructor( throw OpenAIInvalidDataException("'type' is invalid, received $it") } } - passThreshold() range() + passThreshold() validated = true } @@ -1855,1003 +2302,25 @@ private constructor( (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 { - - /** - * 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}" - } + (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/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 000000000..cd49645da --- /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 000000000..6523c6c5b --- /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 000000000..329bf9cb1 --- /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 000000000..c3229eb53 --- /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/jobs/FineTuningJob.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/FineTuningJob.kt index 9c99d13ca..5780ac73a 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 @@ -2357,20 +2360,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 +2393,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 +2459,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 +2564,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 +2589,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 +2612,31 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (dpo.asKnown().getOrNull()?.validity() ?: 0) + - (supervised.asKnown().getOrNull()?.validity() ?: 0) + - (type.asKnown().getOrNull()?.validity() ?: 0) + (type.asKnown().getOrNull()?.validity() ?: 0) + + (dpo.asKnown().getOrNull()?.validity() ?: 0) + + (reinforcement.asKnown().getOrNull()?.validity() ?: 0) + + (supervised.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()) - - /** - * 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 - } - - 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) { + @JvmField val SUPERVISED = of("supervised") - override fun ObjectCodec.deserialize(node: JsonNode): BatchSize { - val json = JsonValue.fromJsonNode(node) + @JvmField val DPO = of("dpo") - 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 +2645,7 @@ private constructor( enum class Known { SUPERVISED, DPO, + REINFORCEMENT, } /** @@ -4826,6 +2660,7 @@ private constructor( enum class Value { SUPERVISED, DPO, + REINFORCEMENT, /** An enum member indicating that [Type] was instantiated with an unknown value. */ _UNKNOWN, } @@ -4841,6 +2676,7 @@ private constructor( when (this) { SUPERVISED -> Value.SUPERVISED DPO -> Value.DPO + REINFORCEMENT -> Value.REINFORCEMENT else -> Value._UNKNOWN } @@ -4857,6 +2693,7 @@ private constructor( when (this) { SUPERVISED -> Known.SUPERVISED DPO -> Known.DPO + REINFORCEMENT -> Known.REINFORCEMENT else -> throw OpenAIInvalidDataException("Unknown Type: $value") } @@ -4919,17 +2756,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/JobCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobCreateParams.kt index 345624c28..f08fe5de7 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/JobPauseParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobPauseParams.kt new file mode 100644 index 000000000..d13a1d5ea --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobPauseParams.kt @@ -0,0 +1,232 @@ +// 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.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 + +/** 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(): String = fineTuningJobId + + fun _additionalBodyProperties(): Map = additionalBodyProperties + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [JobPauseParams]. + * + * The following fields are required: + * ```java + * .fineTuningJobId() + * ``` + */ + @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 + } + + 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. + * + * The following fields are required: + * ```java + * .fineTuningJobId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): JobPauseParams = + JobPauseParams( + checkRequired("fineTuningJobId", 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 000000000..a312b7048 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobResumeParams.kt @@ -0,0 +1,232 @@ +// 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.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 + +/** 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(): String = fineTuningJobId + + fun _additionalBodyProperties(): Map = additionalBodyProperties + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [JobResumeParams]. + * + * The following fields are required: + * ```java + * .fineTuningJobId() + * ``` + */ + @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 + } + + 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. + * + * The following fields are required: + * ```java + * .fineTuningJobId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): JobResumeParams = + JobResumeParams( + checkRequired("fineTuningJobId", 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/methods/DpoHyperparameters.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/methods/DpoHyperparameters.kt new file mode 100644 index 000000000..e3c8b5c34 --- /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 000000000..bc4896342 --- /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 000000000..99840ec72 --- /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 000000000..423082eea --- /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 000000000..92aa0e794 --- /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 000000000..e6aa5f128 --- /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 7576d0c9f..e29ecd157 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 000000000..8b9501898 --- /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 type of grader. + * + * 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 000000000..6aef3d56b --- /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 000000000..b6dc3c821 --- /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 e1279a149..34bf92168 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 271cc2c21..a72392d4d 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/services/async/FineTuningServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/FineTuningServiceAsync.kt index aaa7bc2c4..920bcfccc 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 ca6f76435..59413489e 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 000000000..fdc63d85b --- /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 000000000..28ac64f37 --- /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/finetuning/AlphaServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/AlphaServiceAsync.kt new file mode 100644 index 000000000..58bc3d543 --- /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 000000000..fff90acb5 --- /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 b19633ee2..6575512f6 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 @@ -94,6 +96,26 @@ interface JobServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** Pause a fine-tune job. */ + fun pause(params: JobPauseParams): CompletableFuture = + pause(params, RequestOptions.none()) + + /** @see [pause] */ + fun pause( + params: JobPauseParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** Resume a fine-tune job. */ + fun resume(params: JobResumeParams): CompletableFuture = + resume(params, RequestOptions.none()) + + /** @see [resume] */ + fun resume( + params: JobResumeParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + /** A view of [JobServiceAsync] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -189,5 +211,35 @@ interface JobServiceAsync { params: JobListEventsParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + + /** + * 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(params: JobPauseParams): CompletableFuture> = + pause(params, RequestOptions.none()) + + /** @see [pause] */ + @MustBeClosed + fun pause( + params: JobPauseParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * 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(params: JobResumeParams): CompletableFuture> = + resume(params, RequestOptions.none()) + + /** @see [resume] */ + @MustBeClosed + fun resume( + params: JobResumeParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/JobServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/JobServiceAsyncImpl.kt index 5ebe2d429..72e54fef9 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 @@ -24,6 +24,8 @@ 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 @@ -79,6 +81,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 { @@ -252,5 +268,65 @@ 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> { + 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) + 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> { + 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) + 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 000000000..6c46a35d4 --- /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 000000000..a28030299 --- /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 000000000..a11d77f1d --- /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 000000000..40158b3de --- /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) + 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) + 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/graders/GraderModelServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/graders/GraderModelServiceAsync.kt new file mode 100644 index 000000000..e1d626007 --- /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 000000000..8132d3b8f --- /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/blocking/FineTuningService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/FineTuningService.kt index b63806554..77f45a35d 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 afe68acc7..0f8b31d30 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 000000000..2c9433a24 --- /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 000000000..d6d743053 --- /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/finetuning/AlphaService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/AlphaService.kt new file mode 100644 index 000000000..6f8923640 --- /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 000000000..372892c0a --- /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 1985fe059..cd34aa5d1 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 @@ -90,6 +92,24 @@ interface JobService { requestOptions: RequestOptions = RequestOptions.none(), ): JobListEventsPage + /** Pause a fine-tune job. */ + fun pause(params: JobPauseParams): FineTuningJob = pause(params, RequestOptions.none()) + + /** @see [pause] */ + fun pause( + params: JobPauseParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): FineTuningJob + + /** Resume a fine-tune job. */ + fun resume(params: JobResumeParams): FineTuningJob = resume(params, RequestOptions.none()) + + /** @see [resume] */ + fun resume( + params: JobResumeParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): FineTuningJob + /** A view of [JobService] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -177,5 +197,35 @@ interface JobService { params: JobListEventsParams, requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + + /** + * 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(params: JobPauseParams): HttpResponseFor = + pause(params, RequestOptions.none()) + + /** @see [pause] */ + @MustBeClosed + fun pause( + params: JobPauseParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * 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(params: JobResumeParams): HttpResponseFor = + resume(params, RequestOptions.none()) + + /** @see [resume] */ + @MustBeClosed + fun resume( + params: JobResumeParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor } } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/JobServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/JobServiceImpl.kt index 64319c98b..e632983c1 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 @@ -24,6 +24,8 @@ 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 @@ -66,6 +68,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 { @@ -224,5 +234,59 @@ 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 { + 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) + 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 { + 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) + 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 000000000..7d829c065 --- /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 000000000..a7750b2cf --- /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 000000000..f6e4fe7ca --- /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 000000000..aec8d1f5b --- /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) + 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) + 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/graders/GraderModelService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/graders/GraderModelService.kt new file mode 100644 index 000000000..40f334c4c --- /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 000000000..01f3f0d1a --- /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/test/kotlin/com/openai/models/evals/EvalCreateResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/evals/EvalCreateResponseTest.kt index 8d2a3a39a..208c7d652 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 06becabb8..000000000 --- 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 a063219ca..10d5eddbd 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 a88350116..11f418a8d 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 dcf6e03c2..3838a4d01 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 d15819ad3..000000000 --- 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 f7cc1230b..000000000 --- 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 6a7d1e99f..e4632e7c0 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 000000000..6054610f0 --- /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 000000000..ee806aba7 --- /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 000000000..214773004 --- /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 000000000..7fc0a06d4 --- /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 33c530e68..5d1f8f919 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 @@ -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() @@ -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() ) } @@ -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 f3391493d..28cd1a8b2 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 833580a8a..169aad89d 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 @@ -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() @@ -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() @@ -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 000000000..052bab3e2 --- /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 000000000..e3f90315e --- /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 000000000..d220c3c7c --- /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 000000000..58ef87317 --- /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 000000000..c72042346 --- /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 000000000..271a02bee --- /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 000000000..99b9ec04d --- /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 000000000..93769d44f --- /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 000000000..8243d7362 --- /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 000000000..81423d030 --- /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 000000000..3ac30afbc --- /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 000000000..ff2134b6f --- /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 000000000..6eb0c92b9 --- /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 000000000..f271eeb4f --- /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/services/ErrorHandlingTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/ErrorHandlingTest.kt index f7e594be5..f19d5a985 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/finetuning/JobServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/finetuning/JobServiceAsyncTest.kt index 69cee8abe..c3033d9f2 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 @@ -8,7 +8,16 @@ 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.JobPauseParams +import com.openai.models.finetuning.jobs.JobResumeParams 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 +64,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 +77,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 +113,6 @@ internal class JobServiceAsyncTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) .seed(42L) @@ -159,4 +193,40 @@ internal class JobServiceAsyncTest { 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( + JobPauseParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() + ) + + 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( + JobResumeParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() + ) + + 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 000000000..e6f3f90ed --- /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/blocking/finetuning/JobServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/finetuning/JobServiceTest.kt index fd91a8c0f..e3f3d8a50 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 @@ -8,7 +8,16 @@ 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.JobPauseParams +import com.openai.models.finetuning.jobs.JobResumeParams 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 +64,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 +77,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 +113,6 @@ internal class JobServiceTest { ) .build() ) - .type(JobCreateParams.Method.Type.SUPERVISED) .build() ) .seed(42L) @@ -154,4 +188,38 @@ internal class JobServiceTest { 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( + JobPauseParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() + ) + + 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( + JobResumeParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() + ) + + 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 000000000..8d8ac5e37 --- /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() + } +} From bb85d0d1a899b3981f5c1f818dc4200939cb571d Mon Sep 17 00:00:00 2001 From: Tomer Aberbach Date: Thu, 8 May 2025 18:00:19 -0400 Subject: [PATCH 02/17] fix: add missing `deploymentModel` params --- .../openai/services/async/finetuning/JobServiceAsyncImpl.kt | 4 ++-- .../services/async/finetuning/alpha/GraderServiceAsyncImpl.kt | 4 ++-- .../com/openai/services/blocking/finetuning/JobServiceImpl.kt | 4 ++-- .../services/blocking/finetuning/alpha/GraderServiceImpl.kt | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) 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 72e54fef9..128717f84 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 @@ -282,7 +282,7 @@ class JobServiceAsyncImpl internal constructor(private val clientOptions: Client .addPathSegments("fine_tuning", "jobs", params._pathParam(0), "pause") .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } .build() - .prepareAsync(clientOptions, params) + .prepareAsync(clientOptions, params, deploymentModel = null) val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) return request .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } @@ -312,7 +312,7 @@ class JobServiceAsyncImpl internal constructor(private val clientOptions: Client .addPathSegments("fine_tuning", "jobs", params._pathParam(0), "resume") .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } .build() - .prepareAsync(clientOptions, params) + .prepareAsync(clientOptions, params, deploymentModel = null) val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) return request .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/alpha/GraderServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/alpha/GraderServiceAsyncImpl.kt index 40158b3de..9eb20ee45 100644 --- 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 @@ -62,7 +62,7 @@ class GraderServiceAsyncImpl internal constructor(private val clientOptions: Cli .addPathSegments("fine_tuning", "alpha", "graders", "run") .body(json(clientOptions.jsonMapper, params._body())) .build() - .prepareAsync(clientOptions, params) + .prepareAsync(clientOptions, params, deploymentModel = null) val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) return request .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } @@ -93,7 +93,7 @@ class GraderServiceAsyncImpl internal constructor(private val clientOptions: Cli .addPathSegments("fine_tuning", "alpha", "graders", "validate") .body(json(clientOptions.jsonMapper, params._body())) .build() - .prepareAsync(clientOptions, params) + .prepareAsync(clientOptions, params, deploymentModel = null) val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) return request .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/JobServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/JobServiceImpl.kt index e632983c1..08b3ba5d0 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 @@ -248,7 +248,7 @@ class JobServiceImpl internal constructor(private val clientOptions: ClientOptio .addPathSegments("fine_tuning", "jobs", params._pathParam(0), "pause") .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } .build() - .prepare(clientOptions, params) + .prepare(clientOptions, params, deploymentModel = null) val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) val response = clientOptions.httpClient.execute(request, requestOptions) return response.parseable { @@ -275,7 +275,7 @@ class JobServiceImpl internal constructor(private val clientOptions: ClientOptio .addPathSegments("fine_tuning", "jobs", params._pathParam(0), "resume") .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } .build() - .prepare(clientOptions, params) + .prepare(clientOptions, params, deploymentModel = null) 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/finetuning/alpha/GraderServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/alpha/GraderServiceImpl.kt index aec8d1f5b..4a9e21fc8 100644 --- 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 @@ -58,7 +58,7 @@ class GraderServiceImpl internal constructor(private val clientOptions: ClientOp .addPathSegments("fine_tuning", "alpha", "graders", "run") .body(json(clientOptions.jsonMapper, params._body())) .build() - .prepare(clientOptions, params) + .prepare(clientOptions, params, deploymentModel = null) val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) val response = clientOptions.httpClient.execute(request, requestOptions) return response.parseable { @@ -86,7 +86,7 @@ class GraderServiceImpl internal constructor(private val clientOptions: ClientOp .addPathSegments("fine_tuning", "alpha", "graders", "validate") .body(json(clientOptions.jsonMapper, params._body())) .build() - .prepare(clientOptions, params) + .prepare(clientOptions, params, deploymentModel = null) val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) val response = clientOptions.httpClient.execute(request, requestOptions) return response.parseable { From 7200cf61d31fcc16b15d01cd83d3a0bcc53eba4d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 22:08:26 +0000 Subject: [PATCH 03/17] feat(client): allow providing some params positionally --- README.md | 5 +- .../src/main/kotlin/com/openai/core/Check.kt | 3 + .../models/batches/BatchCancelParams.kt | 33 +- .../models/batches/BatchRetrieveParams.kt | 38 +-- .../beta/assistants/AssistantDeleteParams.kt | 33 +- .../assistants/AssistantRetrieveParams.kt | 34 +- .../beta/assistants/AssistantUpdateParams.kt | 32 +- .../models/beta/threads/ThreadDeleteParams.kt | 33 +- .../beta/threads/ThreadRetrieveParams.kt | 38 +-- .../models/beta/threads/ThreadUpdateParams.kt | 32 +- .../threads/messages/MessageCreateParams.kt | 15 +- .../threads/messages/MessageDeleteParams.kt | 16 +- .../threads/messages/MessageListParams.kt | 32 +- .../threads/messages/MessageRetrieveParams.kt | 17 +- .../threads/messages/MessageUpdateParams.kt | 15 +- .../beta/threads/runs/RunCancelParams.kt | 16 +- .../beta/threads/runs/RunCreateParams.kt | 15 +- .../models/beta/threads/runs/RunListParams.kt | 32 +- .../beta/threads/runs/RunRetrieveParams.kt | 17 +- .../runs/RunSubmitToolOutputsParams.kt | 15 +- .../beta/threads/runs/RunUpdateParams.kt | 15 +- .../beta/threads/runs/steps/StepListParams.kt | 15 +- .../threads/runs/steps/StepRetrieveParams.kt | 15 +- .../completions/ChatCompletionDeleteParams.kt | 29 +- .../ChatCompletionRetrieveParams.kt | 30 +- .../completions/ChatCompletionUpdateParams.kt | 15 +- .../completions/messages/MessageListParams.kt | 32 +- .../openai/models/evals/EvalDeleteParams.kt | 33 +- .../openai/models/evals/EvalRetrieveParams.kt | 38 +-- .../openai/models/evals/EvalUpdateParams.kt | 32 +- .../models/evals/runs/RunCancelParams.kt | 16 +- .../models/evals/runs/RunCreateParams.kt | 15 +- .../models/evals/runs/RunDeleteParams.kt | 16 +- .../openai/models/evals/runs/RunListParams.kt | 32 +- .../models/evals/runs/RunRetrieveParams.kt | 17 +- .../runs/outputitems/OutputItemListParams.kt | 15 +- .../outputitems/OutputItemRetrieveParams.kt | 17 +- .../openai/models/files/FileContentParams.kt | 38 +-- .../openai/models/files/FileDeleteParams.kt | 33 +- .../openai/models/files/FileRetrieveParams.kt | 38 +-- .../permissions/PermissionCreateParams.kt | 20 +- .../permissions/PermissionDeleteParams.kt | 16 +- .../permissions/PermissionRetrieveParams.kt | 36 +- .../models/finetuning/jobs/JobCancelParams.kt | 34 +- .../finetuning/jobs/JobListEventsParams.kt | 33 +- .../models/finetuning/jobs/JobPauseParams.kt | 34 +- .../models/finetuning/jobs/JobResumeParams.kt | 34 +- .../finetuning/jobs/JobRetrieveParams.kt | 35 +- .../jobs/checkpoints/CheckpointListParams.kt | 33 +- .../openai/models/models/ModelDeleteParams.kt | 33 +- .../models/models/ModelRetrieveParams.kt | 38 +-- .../models/responses/ResponseDeleteParams.kt | 33 +- .../responses/ResponseRetrieveParams.kt | 32 +- .../inputitems/InputItemListParams.kt | 32 +- .../models/uploads/UploadCancelParams.kt | 33 +- .../models/uploads/UploadCompleteParams.kt | 15 +- .../models/uploads/parts/PartCreateParams.kt | 17 +- .../vectorstores/VectorStoreDeleteParams.kt | 34 +- .../vectorstores/VectorStoreRetrieveParams.kt | 31 +- .../vectorstores/VectorStoreSearchParams.kt | 16 +- .../vectorstores/VectorStoreUpdateParams.kt | 32 +- .../filebatches/FileBatchCancelParams.kt | 16 +- .../filebatches/FileBatchCreateParams.kt | 16 +- .../filebatches/FileBatchListFilesParams.kt | 15 +- .../filebatches/FileBatchRetrieveParams.kt | 17 +- .../vectorstores/files/FileContentParams.kt | 17 +- .../vectorstores/files/FileCreateParams.kt | 16 +- .../vectorstores/files/FileDeleteParams.kt | 16 +- .../vectorstores/files/FileListParams.kt | 33 +- .../vectorstores/files/FileRetrieveParams.kt | 17 +- .../vectorstores/files/FileUpdateParams.kt | 15 +- .../services/async/BatchServiceAsync.kt | 120 ++++++- .../services/async/BatchServiceAsyncImpl.kt | 8 + .../openai/services/async/EvalServiceAsync.kt | 189 ++++++++++- .../services/async/EvalServiceAsyncImpl.kt | 11 + .../openai/services/async/FileServiceAsync.kt | 183 +++++++++- .../services/async/FileServiceAsyncImpl.kt | 11 + .../services/async/ModelServiceAsync.kt | 119 ++++++- .../services/async/ModelServiceAsyncImpl.kt | 8 + .../services/async/ResponseServiceAsync.kt | 119 ++++++- .../async/ResponseServiceAsyncImpl.kt | 8 + .../services/async/UploadServiceAsync.kt | 89 ++++- .../services/async/UploadServiceAsyncImpl.kt | 8 + .../services/async/VectorStoreServiceAsync.kt | 221 +++++++++++- .../async/VectorStoreServiceAsyncImpl.kt | 14 + .../async/beta/AssistantServiceAsync.kt | 186 +++++++++- .../async/beta/AssistantServiceAsyncImpl.kt | 11 + .../services/async/beta/ThreadServiceAsync.kt | 180 +++++++++- .../async/beta/ThreadServiceAsyncImpl.kt | 11 + .../async/beta/threads/MessageServiceAsync.kt | 179 +++++++++- .../beta/threads/MessageServiceAsyncImpl.kt | 17 + .../async/beta/threads/RunServiceAsync.kt | 266 ++++++++++++++- .../async/beta/threads/RunServiceAsyncImpl.kt | 26 ++ .../beta/threads/runs/StepServiceAsync.kt | 58 ++++ .../beta/threads/runs/StepServiceAsyncImpl.kt | 8 + .../async/chat/ChatCompletionServiceAsync.kt | 160 ++++++++- .../chat/ChatCompletionServiceAsyncImpl.kt | 10 + .../chat/completions/MessageServiceAsync.kt | 63 +++- .../completions/MessageServiceAsyncImpl.kt | 5 + .../services/async/evals/RunServiceAsync.kt | 176 +++++++++- .../async/evals/RunServiceAsyncImpl.kt | 17 + .../evals/runs/OutputItemServiceAsync.kt | 63 ++++ .../evals/runs/OutputItemServiceAsyncImpl.kt | 8 + .../async/finetuning/JobServiceAsync.kt | 318 ++++++++++++++++-- .../async/finetuning/JobServiceAsyncImpl.kt | 17 + .../checkpoints/PermissionServiceAsync.kt | 142 +++++++- .../checkpoints/PermissionServiceAsyncImpl.kt | 11 + .../finetuning/jobs/CheckpointServiceAsync.kt | 66 +++- .../jobs/CheckpointServiceAsyncImpl.kt | 5 + .../async/responses/InputItemServiceAsync.kt | 63 +++- .../responses/InputItemServiceAsyncImpl.kt | 5 + .../async/uploads/PartServiceAsync.kt | 29 ++ .../async/uploads/PartServiceAsyncImpl.kt | 5 + .../vectorstores/FileBatchServiceAsync.kt | 126 +++++++ .../vectorstores/FileBatchServiceAsyncImpl.kt | 14 + .../async/vectorstores/FileServiceAsync.kt | 214 +++++++++++- .../vectorstores/FileServiceAsyncImpl.kt | 20 ++ .../openai/services/blocking/BatchService.kt | 100 +++++- .../services/blocking/BatchServiceImpl.kt | 8 + .../openai/services/blocking/EvalService.kt | 167 ++++++++- .../services/blocking/EvalServiceImpl.kt | 11 + .../openai/services/blocking/FileService.kt | 156 ++++++++- .../services/blocking/FileServiceImpl.kt | 11 + .../openai/services/blocking/ModelService.kt | 100 +++++- .../services/blocking/ModelServiceImpl.kt | 8 + .../services/blocking/ResponseService.kt | 104 +++++- .../services/blocking/ResponseServiceImpl.kt | 8 + .../openai/services/blocking/UploadService.kt | 75 ++++- .../services/blocking/UploadServiceImpl.kt | 8 + .../services/blocking/VectorStoreService.kt | 204 ++++++++++- .../blocking/VectorStoreServiceImpl.kt | 14 + .../blocking/beta/AssistantService.kt | 172 +++++++++- .../blocking/beta/AssistantServiceImpl.kt | 11 + .../services/blocking/beta/ThreadService.kt | 157 ++++++++- .../blocking/beta/ThreadServiceImpl.kt | 11 + .../blocking/beta/threads/MessageService.kt | 157 ++++++++- .../beta/threads/MessageServiceImpl.kt | 17 + .../blocking/beta/threads/RunService.kt | 242 ++++++++++++- .../blocking/beta/threads/RunServiceImpl.kt | 26 ++ .../blocking/beta/threads/runs/StepService.kt | 50 +++ .../beta/threads/runs/StepServiceImpl.kt | 8 + .../blocking/chat/ChatCompletionService.kt | 147 +++++++- .../chat/ChatCompletionServiceImpl.kt | 10 + .../chat/completions/MessageService.kt | 55 ++- .../chat/completions/MessageServiceImpl.kt | 5 + .../services/blocking/evals/RunService.kt | 151 ++++++++- .../services/blocking/evals/RunServiceImpl.kt | 17 + .../blocking/evals/runs/OutputItemService.kt | 56 +++ .../evals/runs/OutputItemServiceImpl.kt | 8 + .../blocking/finetuning/JobService.kt | 288 +++++++++++++++- .../blocking/finetuning/JobServiceImpl.kt | 17 + .../checkpoints/PermissionService.kt | 138 +++++++- .../checkpoints/PermissionServiceImpl.kt | 11 + .../finetuning/jobs/CheckpointService.kt | 58 +++- .../finetuning/jobs/CheckpointServiceImpl.kt | 5 + .../blocking/responses/InputItemService.kt | 55 ++- .../responses/InputItemServiceImpl.kt | 5 + .../services/blocking/uploads/PartService.kt | 25 ++ .../blocking/uploads/PartServiceImpl.kt | 5 + .../blocking/vectorstores/FileBatchService.kt | 112 ++++++ .../vectorstores/FileBatchServiceImpl.kt | 14 + .../blocking/vectorstores/FileService.kt | 183 +++++++++- .../blocking/vectorstores/FileServiceImpl.kt | 20 ++ .../services/async/BatchServiceAsyncTest.kt | 8 +- .../services/async/EvalServiceAsyncTest.kt | 8 +- .../services/async/FileServiceAsyncTest.kt | 12 +- .../services/async/ModelServiceAsyncTest.kt | 10 +- .../async/ResponseServiceAsyncTest.kt | 8 +- .../services/async/UploadServiceAsyncTest.kt | 6 +- .../async/VectorStoreServiceAsyncTest.kt | 12 +- .../async/beta/AssistantServiceAsyncTest.kt | 12 +- .../async/beta/ThreadServiceAsyncTest.kt | 10 +- .../beta/threads/MessageServiceAsyncTest.kt | 4 +- .../async/beta/threads/RunServiceAsyncTest.kt | 3 +- .../chat/ChatCompletionServiceAsyncTest.kt | 12 +- .../completions/MessageServiceAsyncTest.kt | 6 +- .../async/evals/RunServiceAsyncTest.kt | 3 +- .../async/finetuning/JobServiceAsyncTest.kt | 30 +- .../jobs/CheckpointServiceAsyncTest.kt | 8 +- .../responses/InputItemServiceAsyncTest.kt | 6 +- .../vectorstores/FileServiceAsyncTest.kt | 4 +- .../services/blocking/BatchServiceTest.kt | 6 +- .../services/blocking/EvalServiceTest.kt | 6 +- .../services/blocking/FileServiceTest.kt | 10 +- .../services/blocking/ModelServiceTest.kt | 10 +- .../services/blocking/ResponseServiceTest.kt | 7 +- .../services/blocking/UploadServiceTest.kt | 4 +- .../blocking/VectorStoreServiceTest.kt | 12 +- .../blocking/beta/AssistantServiceTest.kt | 12 +- .../blocking/beta/ThreadServiceTest.kt | 8 +- .../beta/threads/MessageServiceTest.kt | 3 +- .../blocking/beta/threads/RunServiceTest.kt | 3 +- .../chat/ChatCompletionServiceTest.kt | 12 +- .../chat/completions/MessageServiceTest.kt | 4 +- .../services/blocking/evals/RunServiceTest.kt | 3 +- .../blocking/finetuning/JobServiceTest.kt | 30 +- .../finetuning/jobs/CheckpointServiceTest.kt | 8 +- .../responses/InputItemServiceTest.kt | 4 +- .../blocking/vectorstores/FileServiceTest.kt | 4 +- 199 files changed, 7333 insertions(+), 1533 deletions(-) diff --git a/README.md b/README.md index 6a05116a5..81f8df02b 100644 --- a/README.md +++ b/README.md @@ -412,10 +412,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: 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 dc411d75d..53b3b919e 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/models/batches/BatchCancelParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchCancelParams.kt index c9a0e7250..4fd0d88f5 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/BatchRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchRetrieveParams.kt index 94571a8a2..d1667cc1d 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 f0c6d40fb..b5df03cb3 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/AssistantRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantRetrieveParams.kt index 5ded97d81..7b9b07924 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 92fcba310..24ae8550c 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 fd960288b..637a58534 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 1d5bb1ae7..2ee637c69 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 3afece775..d6d639af8 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 6bf8f054c..caca9be56 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 e20225101..9c79b7b31 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/MessageListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListParams.kt index 0a1f8bbbc..add2694aa 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 fc521fbfa..4c9cb0932 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 de35e85eb..302718712 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 cf442b6ca..ccb81100c 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 3d44da707..02f162705 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/RunListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListParams.kt index 22d967142..cd4e0d5d1 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 850f086c4..1ffc8e41f 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 38e059987..8b23d6c91 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 b93a12350..21ecf31a5 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/StepListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListParams.kt index ca18dea9b..c88cb3ef2 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 15275de57..c42a51a5d 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/ChatCompletionDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionDeleteParams.kt index 0cd4d7faa..64f6792e1 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/ChatCompletionRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionRetrieveParams.kt index adf67c89d..5254f338b 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 953298850..6468e4e71 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/messages/MessageListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListParams.kt index 0f7c3f25f..a60bc6d6e 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/evals/EvalDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalDeleteParams.kt index 7220bde2a..74f97a4ee 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/EvalRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalRetrieveParams.kt index 63111243d..3a411a0bc 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/EvalUpdateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalUpdateParams.kt index c5ea36945..d3556b083 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/runs/RunCancelParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCancelParams.kt index 63c37ab08..05903cbdf 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/RunCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunCreateParams.kt index 952e12bab..a50d3f6dd 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 @@ -39,13 +39,13 @@ import kotlin.jvm.optionals.getOrNull /** Create a new evaluation run. This is the endpoint that will kick off grading. */ 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 +112,6 @@ private constructor( * * The following fields are required: * ```java - * .evalId() * .dataSource() * ``` */ @@ -135,7 +134,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 +344,6 @@ private constructor( * * The following fields are required: * ```java - * .evalId() * .dataSource() * ``` * @@ -350,7 +351,7 @@ private constructor( */ fun build(): RunCreateParams = RunCreateParams( - checkRequired("evalId", evalId), + evalId, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -361,7 +362,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/RunDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunDeleteParams.kt index 2214b0d23..784ca9c03 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/RunListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListParams.kt index b825f64b6..56035b50d 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/RunRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunRetrieveParams.kt index b5cfc5002..26e388e7c 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/outputitems/OutputItemListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemListParams.kt index 4c6c896be..d7b882106 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 3d1a3ce32..361599f5b 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 0a4ea8abb..5035b1689 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 2bc93b6a9..dca35f8a6 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/FileRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/files/FileRetrieveParams.kt index 379eb2f10..cb00e7084 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/checkpoints/permissions/PermissionCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionCreateParams.kt index 6346ac328..dcbd6ccd9 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 bf8173c54..ce09abfca 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 e9276feef..2205d7673 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/JobCancelParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobCancelParams.kt index 4d7c9f96c..a4fbd422f 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/JobListEventsParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsParams.kt index 26348572d..1922578dc 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/JobPauseParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobPauseParams.kt index d13a1d5ea..d4cc700b2 100644 --- 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 @@ -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 /** Pause a fine-tune job. */ class JobPauseParams 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 [JobPauseParams]. - * - * The following fields are required: - * ```java - * .fineTuningJobId() - * ``` - */ + @JvmStatic fun none(): JobPauseParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [JobPauseParams]. */ @JvmStatic fun builder() = Builder() } @@ -59,10 +54,14 @@ private constructor( additionalBodyProperties = jobPauseParams.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 [JobPauseParams]. * * 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(): JobPauseParams = JobPauseParams( - 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/JobResumeParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobResumeParams.kt index a312b7048..97f214e36 100644 --- 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 @@ -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 /** Resume a fine-tune job. */ class JobResumeParams 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 [JobResumeParams]. - * - * The following fields are required: - * ```java - * .fineTuningJobId() - * ``` - */ + @JvmStatic fun none(): JobResumeParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [JobResumeParams]. */ @JvmStatic fun builder() = Builder() } @@ -59,10 +54,14 @@ private constructor( additionalBodyProperties = jobResumeParams.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 [JobResumeParams]. * * 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(): JobResumeParams = JobResumeParams( - 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/JobRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobRetrieveParams.kt index 2e5e28749..3a5e08147 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/CheckpointListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListParams.kt index 917d04586..46483d0c3 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/models/ModelDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/models/ModelDeleteParams.kt index f87f9a5ae..c3b48aed3 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/ModelRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/models/ModelRetrieveParams.kt index d76cdabc1..1be641852 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/ResponseDeleteParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseDeleteParams.kt index b3fae35d2..38398d98a 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/ResponseRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseRetrieveParams.kt index 3c73de870..bc1589495 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/inputitems/InputItemListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListParams.kt index 11ddf361f..e3f3cbd98 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 5b2fe821b..013b5ddcc 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 990ef1d4d..fb5603c1e 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 a396a730a..791713e4f 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 efd70d98d..b6bbda6ea 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/VectorStoreRetrieveParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreRetrieveParams.kt index f8fc087de..87d3f482c 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/VectorStoreSearchParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchParams.kt index 849e5ab3d..0add3f175 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 e28e249ac..905256cb2 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 1cc885126..e317488b1 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 6109a4f18..e515321c0 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/FileBatchListFilesParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesParams.kt index 2be57ce8b..e0b95a1db 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 de2b16bd8..8a3acc77b 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/FileContentParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentParams.kt index e52fe40d0..245032132 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 339559a04..d6c0af37a 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 7ac510f21..c739cb626 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/FileListParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListParams.kt index 4f0240de9..c2ca930cd 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 5ab124552..f81b11273 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 c5dbccbde..2377d4d26 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 45dd0c246..84bc4043c 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 82fbc507d..18cb8d652 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) @@ -169,6 +174,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 8c2fad82c..d3b270753 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 @@ -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 46b90a997..24a47d359 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) @@ -223,6 +231,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 1da62fdff..6cc13c80f 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 a60ded873..92fc64a69 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) @@ -180,6 +185,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 +215,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/ModelServiceAsync.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/ModelServiceAsync.kt index 33205d210..993f48db5 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 7973eaa3f..1a5d178b5 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,6 +68,9 @@ 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) @@ -132,6 +137,9 @@ 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) 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 34042fd28..f66149de6 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 122e68f43..7f90cea5a 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 7e0dce0ce..98218ac53 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 9877742d4..30cc034a8 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 4fb10a20a..ff07354c2 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 da248f22b..e33c2655d 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) @@ -254,6 +262,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 +297,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) 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 84e33a144..b36485ed4 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 7eb203939..64a77f919 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) @@ -222,6 +230,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 76a89f632..5eaf86429 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 c8334900e..a0193523c 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 364851ad8..4ec7a1515 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 9753c5f62..d47f76a3d 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) @@ -228,6 +242,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 ca8f68ad5..660d74de5 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 9a15dc341..a4292021c 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) @@ -317,6 +334,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 +374,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 +417,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 3718a766d..6f92544ac 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 9f3f8350c..623f52770 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) 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 50e7adffa..2890cb312 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 783a608dc..8b6816bb1 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) @@ -287,6 +294,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 3c5d17d36..6edcc5a02 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 c66410b5c..f2b8ced35 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) 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 801e00e47..630a6e697 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 @@ -28,6 +28,18 @@ interface RunServiceAsync { fun outputItems(): OutputItemServiceAsync /** Create a new evaluation run. This is the endpoint that will kick off grading. */ + 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 +50,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 +72,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 +95,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 +126,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 +157,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 +189,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 +223,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 +250,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 +300,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 b39590a1d..f8489292f 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) @@ -195,6 +206,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 +239,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 705932c08..6373f511b 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 05537bedf..9163a7b40 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) 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 6575512f6..9ed467088 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 @@ -50,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( @@ -59,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()) @@ -77,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( @@ -86,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( @@ -96,9 +161,34 @@ 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(params: JobPauseParams): CompletableFuture = - pause(params, RequestOptions.none()) + 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( @@ -106,9 +196,34 @@ interface JobServiceAsync { 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(params: JobResumeParams): CompletableFuture = - resume(params, RequestOptions.none()) + 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( @@ -116,6 +231,17 @@ interface JobServiceAsync { 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 { @@ -141,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 @@ -151,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]. @@ -185,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 @@ -195,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 @@ -212,13 +415,45 @@ interface JobServiceAsync { 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(params: JobPauseParams): CompletableFuture> = - pause(params, RequestOptions.none()) + 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 @@ -227,13 +462,43 @@ interface JobServiceAsync { 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(params: JobResumeParams): CompletableFuture> = - resume(params, RequestOptions.none()) + 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 @@ -241,5 +506,18 @@ interface JobServiceAsync { 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 128717f84..be6201616 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 @@ -30,6 +31,7 @@ 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 { @@ -143,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) @@ -209,6 +214,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) @@ -240,6 +248,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) @@ -276,6 +287,9 @@ class JobServiceAsyncImpl internal constructor(private val clientOptions: Client 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) @@ -306,6 +320,9 @@ class JobServiceAsyncImpl internal constructor(private val clientOptions: Client 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) 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 6fa0d1541..0d293bcb9 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 9d763dd76..f0c1b455f 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) @@ -110,6 +115,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 +153,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 a479aa81c..b53b92a99 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 610145095..c596bf4d7 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) 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 c0c1abfd4..11b92dbf1 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 32f8112ec..180766980 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) 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 2fd896865..7961d9b68 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 6fddaa5a9..381fb5fb5 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 9eb126530..df0a4a2d4 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 e9d7625a5..f349afb5e 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) 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 27a654c38..bc0b1e49b 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 a03202c6d..18fe38e5a 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) @@ -239,6 +253,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 +293,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/blocking/BatchService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/BatchService.kt index 6c66bb582..778efa587 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 7a1a929cf..51717e40d 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 594a1ffc9..f1af3c4cb 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 @@ -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 faa4cc5fd..9fda99b9c 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 c952d6afa..3f4934c38 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 c9b1f1f78..0f9970290 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/ModelService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModelService.kt index 7b34885ad..d6550622d 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 6e7a023ea..656d61823 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,6 +58,9 @@ 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) @@ -116,6 +121,9 @@ 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) 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 acced25c9..b6e1f9620 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 @@ -65,7 +65,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 +86,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 +159,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 +185,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 +227,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 b683a7d69..de7f11be5 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 47938c0ec..17bc71b0f 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 d28416443..6b2d77337 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 b61e9ce25..e5256a03a 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 5a0770917..893276794 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 2943bacef..98d5e1950 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 521543aa9..30ca6d1be 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 8263aa931..a36261115 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 5d7ec9222..fa03e9bdb 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 87184f956..86e7ea88e 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 fddd1451a..83e8bc6cb 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 02562d7ee..0708353b4 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 0d7f999db..0f7f56e81 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 5b4bf4718..6e5270de1 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 e4d41faf6..bc2477b00 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 3f46a970e..8febcf124 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 @@ -86,8 +86,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 +109,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 +168,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 +191,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 +243,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 +269,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 +343,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 +369,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 0bffd8dfd..b07c2c6c5 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 b1161981b..d4fc968ca 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 c87f53ee9..52b267d3d 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 c604c6c21..fff6cf21f 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 @@ -27,6 +27,17 @@ interface RunService { fun outputItems(): OutputItemService /** Create a new evaluation run. This is the endpoint that will kick off grading. */ + 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 +47,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 +68,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 +87,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 +115,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 +144,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 +173,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 +204,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 +229,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 +273,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 aa985644a..ee64dae6e 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 5a1750da2..59468f9e7 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 cce944dd3..de2cfba3b 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/JobService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/finetuning/JobService.kt index cd34aa5d1..785543c00 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 @@ -48,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( @@ -56,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()) @@ -74,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( @@ -82,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( @@ -92,8 +150,31 @@ 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(params: JobPauseParams): FineTuningJob = pause(params, RequestOptions.none()) + 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( @@ -101,8 +182,30 @@ interface JobService { 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(params: JobResumeParams): FineTuningJob = resume(params, RequestOptions.none()) + 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( @@ -110,6 +213,13 @@ interface JobService { 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 { @@ -135,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 @@ -145,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]. @@ -173,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 @@ -183,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 @@ -198,13 +383,42 @@ interface JobService { 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(params: JobPauseParams): HttpResponseFor = - pause(params, RequestOptions.none()) + 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 @@ -213,13 +427,42 @@ interface JobService { 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(params: JobResumeParams): HttpResponseFor = - resume(params, RequestOptions.none()) + 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 @@ -227,5 +470,18 @@ interface JobService { 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 08b3ba5d0..ba95b5d36 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 @@ -29,6 +30,7 @@ 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 { @@ -121,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) @@ -181,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) @@ -209,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) @@ -242,6 +253,9 @@ class JobServiceImpl internal constructor(private val clientOptions: ClientOptio 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) @@ -269,6 +283,9 @@ class JobServiceImpl internal constructor(private val clientOptions: ClientOptio 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) 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 3271fff5d..547aa2659 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 7b8092686..6ac2f12e8 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 125b06c98..487177734 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 34d91dc8c..23fe2d2d2 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/responses/InputItemService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/responses/InputItemService.kt index bbfab36ef..81690e164 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 19e1a397f..aa535bbc7 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 1c157e709..a19904934 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 0f616b5db..23bf0b689 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 5bf2fe582..2f9f55f5c 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 516848c43..edfa1dd21 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 1d081a12c..9cb49b698 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 c3e9174af..971cf72ed 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/services/async/BatchServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/BatchServiceAsyncTest.kt index 25228c552..bddf750dd 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 b4a7c4fb2..1f940b245 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 6c8af86c5..0156759e4 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 25b7ab235..95ab7c7f0 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 a7f6183b4..8e6658e3c 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 0b652b221..48edbd353 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 90a9bf882..63306b105 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/beta/AssistantServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/beta/AssistantServiceAsyncTest.kt index b731d4938..bf5eebb46 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 cb993034a..91ba504c5 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 14c21f5c2..088486d7e 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 70bdc4c70..51885c40e 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 029284428..693c02d26 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 5fbbb2fa3..2c834f1f2 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 22e9d8e98..2f6212977 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 c3033d9f2..6a9b1aef6 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,12 +5,7 @@ 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.JobPauseParams -import com.openai.models.finetuning.jobs.JobResumeParams -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 @@ -134,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() @@ -167,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() @@ -185,10 +174,7 @@ 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() @@ -203,10 +189,7 @@ internal class JobServiceAsyncTest { .build() val jobServiceAsync = client.fineTuning().jobs() - val fineTuningJobFuture = - jobServiceAsync.pause( - JobPauseParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() - ) + val fineTuningJobFuture = jobServiceAsync.pause("ft-AF1WoRqd3aJAHsqc9NY7iL8F") val fineTuningJob = fineTuningJobFuture.get() fineTuningJob.validate() @@ -221,10 +204,7 @@ internal class JobServiceAsyncTest { .build() val jobServiceAsync = client.fineTuning().jobs() - val fineTuningJobFuture = - jobServiceAsync.resume( - JobResumeParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() - ) + 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/jobs/CheckpointServiceAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/async/finetuning/jobs/CheckpointServiceAsyncTest.kt index ff0521c89..75bfa2d21 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 241f678df..2c2953768 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 fdcfc5511..b9564abe1 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 04ad53cdc..1a7983298 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 940e08228..61e2c4874 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 673fbbb26..ff77adfc8 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 4d899a1b5..f00b554d7 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 e783dcb75..e8ae64524 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 1affd622e..26afefc1b 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 8c12104ee..ba6b6886c 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/beta/AssistantServiceTest.kt b/openai-java-core/src/test/kotlin/com/openai/services/blocking/beta/AssistantServiceTest.kt index d6381fc68..99bf5bf28 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 aefa73189..9cba341ac 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 be07d6559..38c0580d0 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 6394703ef..6d094b7dc 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 bec6b5bb2..9475a0eba 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 f591070d7..3a1a7f103 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 42a3eaaf3..ded7e5942 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 e3f3d8a50..662a78c5e 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,12 +5,7 @@ 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.JobPauseParams -import com.openai.models.finetuning.jobs.JobResumeParams -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 @@ -133,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() } @@ -164,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() } @@ -181,10 +170,7 @@ 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() } @@ -198,10 +184,7 @@ internal class JobServiceTest { .build() val jobService = client.fineTuning().jobs() - val fineTuningJob = - jobService.pause( - JobPauseParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() - ) + val fineTuningJob = jobService.pause("ft-AF1WoRqd3aJAHsqc9NY7iL8F") fineTuningJob.validate() } @@ -215,10 +198,7 @@ internal class JobServiceTest { .build() val jobService = client.fineTuning().jobs() - val fineTuningJob = - jobService.resume( - JobResumeParams.builder().fineTuningJobId("ft-AF1WoRqd3aJAHsqc9NY7iL8F").build() - ) + val fineTuningJob = jobService.resume("ft-AF1WoRqd3aJAHsqc9NY7iL8F") fineTuningJob.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 3ec97abb3..0cb543e5e 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 7d3f09f0d..fa48569e8 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 ae9bdeeb6..2c0f8c7eb 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() } From 458773748bba0efefce9e67d17b8d2879338cb61 Mon Sep 17 00:00:00 2001 From: Tomer Aberbach Date: Thu, 8 May 2025 18:33:20 -0400 Subject: [PATCH 04/17] fix: merge conflict --- .../kotlin/com/openai/services/async/ModelServiceAsyncImpl.kt | 4 ++-- .../kotlin/com/openai/services/blocking/ModelServiceImpl.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 1a5d178b5..07acf38a3 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 @@ -76,7 +76,7 @@ class ModelServiceAsyncImpl internal constructor(private val clientOptions: Clie .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) } @@ -146,7 +146,7 @@ class ModelServiceAsyncImpl internal constructor(private val clientOptions: Clie .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/blocking/ModelServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ModelServiceImpl.kt index 656d61823..7104c2edb 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 @@ -66,7 +66,7 @@ class ModelServiceImpl internal constructor(private val clientOptions: ClientOpt .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 { @@ -130,7 +130,7 @@ class ModelServiceImpl internal constructor(private val clientOptions: ClientOpt .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 { From 4bf868a717f9f782cd2f288bffc74bb24b2bb0e7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 23:06:10 +0000 Subject: [PATCH 05/17] docs: remove or fix invalid readme examples --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 81f8df02b..d13377d58 100644 --- a/README.md +++ b/README.md @@ -772,11 +772,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(); ``` From f623bcac1e66ed15f8ba6c89375468b764cb900f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 23:51:46 +0000 Subject: [PATCH 06/17] feat(client)!: extract auto pagination to shared classes refactor(client)!: refactor async auto-pagination refactor(client)!: rename `getNextPage{,Params}` to `nextPage{,Params}` refactor(client)!: swap `nextPage{,Params}` to return non-optional # 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) There are examples and further information about pagination in the readme. --- README.md | 86 +++++++-- .../main/kotlin/com/openai/core/AutoPager.kt | 21 ++ .../kotlin/com/openai/core/AutoPagerAsync.kt | 88 +++++++++ .../src/main/kotlin/com/openai/core/Page.kt | 33 ++++ .../main/kotlin/com/openai/core/PageAsync.kt | 35 ++++ .../openai/models/batches/BatchListPage.kt | 40 +--- .../models/batches/BatchListPageAsync.kt | 65 +++---- .../beta/assistants/AssistantListPage.kt | 40 +--- .../beta/assistants/AssistantListPageAsync.kt | 66 +++---- .../beta/threads/messages/MessageListPage.kt | 40 +--- .../threads/messages/MessageListPageAsync.kt | 66 +++---- .../models/beta/threads/runs/RunListPage.kt | 40 +--- .../beta/threads/runs/RunListPageAsync.kt | 65 +++---- .../beta/threads/runs/steps/StepListPage.kt | 40 +--- .../threads/runs/steps/StepListPageAsync.kt | 65 +++---- .../completions/ChatCompletionListPage.kt | 41 +--- .../ChatCompletionListPageAsync.kt | 70 +++---- .../completions/messages/MessageListPage.kt | 40 +--- .../messages/MessageListPageAsync.kt | 70 +++---- .../com/openai/models/evals/EvalListPage.kt | 40 +--- .../openai/models/evals/EvalListPageAsync.kt | 69 +++---- .../openai/models/evals/runs/RunListPage.kt | 40 +--- .../models/evals/runs/RunListPageAsync.kt | 69 +++---- .../runs/outputitems/OutputItemListPage.kt | 40 +--- .../outputitems/OutputItemListPageAsync.kt | 70 +++---- .../com/openai/models/files/FileListPage.kt | 40 +--- .../openai/models/files/FileListPageAsync.kt | 65 +++---- .../permissions/PermissionCreatePage.kt | 39 +--- .../permissions/PermissionCreatePageAsync.kt | 67 +++---- .../finetuning/jobs/JobListEventsPage.kt | 41 +--- .../finetuning/jobs/JobListEventsPageAsync.kt | 70 +++---- .../models/finetuning/jobs/JobListPage.kt | 40 +--- .../finetuning/jobs/JobListPageAsync.kt | 66 +++---- .../jobs/checkpoints/CheckpointListPage.kt | 40 +--- .../checkpoints/CheckpointListPageAsync.kt | 70 +++---- .../com/openai/models/models/ModelListPage.kt | 37 +--- .../models/models/ModelListPageAsync.kt | 62 +++--- .../responses/inputitems/InputItemListPage.kt | 129 +++++-------- .../inputitems/InputItemListPageAsync.kt | 155 +++++++-------- .../vectorstores/VectorStoreListPage.kt | 40 +--- .../vectorstores/VectorStoreListPageAsync.kt | 66 +++---- .../vectorstores/VectorStoreSearchPage.kt | 39 +--- .../VectorStoreSearchPageAsync.kt | 67 +++---- .../filebatches/FileBatchListFilesPage.kt | 41 +--- .../FileBatchListFilesPageAsync.kt | 70 +++---- .../vectorstores/files/FileContentPage.kt | 37 +--- .../files/FileContentPageAsync.kt | 67 +++---- .../models/vectorstores/files/FileListPage.kt | 40 +--- .../vectorstores/files/FileListPageAsync.kt | 69 +++---- .../services/async/BatchServiceAsyncImpl.kt | 1 + .../services/async/EvalServiceAsyncImpl.kt | 1 + .../services/async/FileServiceAsyncImpl.kt | 1 + .../services/async/ModelServiceAsyncImpl.kt | 1 + .../async/VectorStoreServiceAsyncImpl.kt | 2 + .../async/beta/AssistantServiceAsyncImpl.kt | 1 + .../beta/threads/MessageServiceAsyncImpl.kt | 1 + .../async/beta/threads/RunServiceAsyncImpl.kt | 1 + .../beta/threads/runs/StepServiceAsyncImpl.kt | 1 + .../chat/ChatCompletionServiceAsyncImpl.kt | 1 + .../completions/MessageServiceAsyncImpl.kt | 1 + .../async/evals/RunServiceAsyncImpl.kt | 1 + .../evals/runs/OutputItemServiceAsyncImpl.kt | 1 + .../async/finetuning/JobServiceAsyncImpl.kt | 2 + .../checkpoints/PermissionServiceAsyncImpl.kt | 1 + .../jobs/CheckpointServiceAsyncImpl.kt | 1 + .../responses/InputItemServiceAsyncImpl.kt | 1 + .../vectorstores/FileBatchServiceAsyncImpl.kt | 1 + .../vectorstores/FileServiceAsyncImpl.kt | 2 + .../com/openai/core/AutoPagerAsyncTest.kt | 182 ++++++++++++++++++ .../kotlin/com/openai/core/AutoPagerTest.kt | 41 ++++ 70 files changed, 1289 insertions(+), 1752 deletions(-) create mode 100644 openai-java-core/src/main/kotlin/com/openai/core/AutoPager.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/core/AutoPagerAsync.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/core/Page.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/core/PageAsync.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/core/AutoPagerAsyncTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/core/AutoPagerTest.kt diff --git a/README.md b/README.md index d13377d58..39125ce8b 100644 --- a/README.md +++ b/README.md @@ -525,53 +525,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(); } ``` @@ -654,9 +702,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() @@ -907,9 +953,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/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 000000000..888fd0347 --- /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 000000000..649be47c9 --- /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/Page.kt b/openai-java-core/src/main/kotlin/com/openai/core/Page.kt new file mode 100644 index 000000000..9c9560b41 --- /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 000000000..c67ff7916 --- /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/models/batches/BatchListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchListPage.kt index 35c7aa3f1..d591ebcca 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 616127122..0b36c622f 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/beta/assistants/AssistantListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/assistants/AssistantListPage.kt index c852b6fea..596b6fa64 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 0e90b27c2..ce42fb78d 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/threads/messages/MessageListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/messages/MessageListPage.kt index 14a9a44dc..560ff950f 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 fd1595e31..8b6a0d4ea 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/runs/RunListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/RunListPage.kt index 108f1287e..206a0e6ae 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 ca329d17d..e5e704662 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/steps/StepListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/beta/threads/runs/steps/StepListPage.kt index b9740fabf..353febb1f 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 a753633ed..8e5b30095 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/chat/completions/ChatCompletionListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionListPage.kt index 6c807633b..a0c724746 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 87bef9ed9..ebd91537c 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/messages/MessageListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/messages/MessageListPage.kt index 4088b3eff..bc6f6aa63 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 d013eff23..7baa95a6f 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/evals/EvalListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/EvalListPage.kt index aa65e3f97..38d95a847 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 d23299f44..c98e16f52 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/runs/RunListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/RunListPage.kt index ca22d061c..4e9fc0d74 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 f159f3923..a914663d6 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/outputitems/OutputItemListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/evals/runs/outputitems/OutputItemListPage.kt index e24395a2d..b75e0d39b 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 9e5c96350..c12bf482b 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/files/FileListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/files/FileListPage.kt index 2aa16da39..9310e1348 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 ec8e2d6c5..7f775f0c3 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/finetuning/checkpoints/permissions/PermissionCreatePage.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/checkpoints/permissions/PermissionCreatePage.kt index ea6aeac9c..fd72758d2 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 174dc4c7a..c67b8a800 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/jobs/JobListEventsPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListEventsPage.kt index 7acc932f3..2a93a6c82 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 eceb15680..2dc4a3e23 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/JobListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/JobListPage.kt index db6bcab0f..7ed9ed478 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 7730d2201..5860505d9 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/checkpoints/CheckpointListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/finetuning/jobs/checkpoints/CheckpointListPage.kt index 64c6ce8be..e5df9890c 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 b5812ee75..4c377a567 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/models/ModelListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/models/ModelListPage.kt index 2da80544d..9ce4bde9f 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 653a08f5a..335ac20c7 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/responses/inputitems/InputItemListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/inputitems/InputItemListPage.kt index 1fbb02eff..6218c55b3 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 ddde7ff4d..a840b3108 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/vectorstores/VectorStoreListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreListPage.kt index fcb7958e9..52852ddb2 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 1e2daf96a..785cf9973 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/VectorStoreSearchPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/VectorStoreSearchPage.kt index fcdefb6c5..a45345a06 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 0b7c1d85c..00431ef94 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/filebatches/FileBatchListFilesPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/filebatches/FileBatchListFilesPage.kt index 42d724d50..96a6d8b35 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 7c52c45ba..c6ea04c1f 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/files/FileContentPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileContentPage.kt index 6bbd30842..e1980ef93 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 a55657f17..30fb9db26 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/FileListPage.kt b/openai-java-core/src/main/kotlin/com/openai/models/vectorstores/files/FileListPage.kt index 51c032618..d77b54c03 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 ace12328d..f4211867e 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/services/async/BatchServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/BatchServiceAsyncImpl.kt index 18cb8d652..bcd741103 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 @@ -159,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() 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 24a47d359..8ceac275b 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 @@ -216,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() 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 92fc64a69..83a8c11c1 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 @@ -170,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() 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 07acf38a3..9f15cdea3 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 @@ -122,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() 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 e33c2655d..346bb0737 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 @@ -247,6 +247,7 @@ class VectorStoreServiceAsyncImpl internal constructor(private val clientOptions .let { VectorStoreListPageAsync.builder() .service(VectorStoreServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() @@ -323,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/AssistantServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/AssistantServiceAsyncImpl.kt index 64a77f919..d7d746f2d 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 @@ -215,6 +215,7 @@ class AssistantServiceAsyncImpl internal constructor(private val clientOptions: .let { AssistantListPageAsync.builder() .service(AssistantServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() 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 d47f76a3d..07aad9bef 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 @@ -227,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() 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 a4292021c..34ff48554 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 @@ -319,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() 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 623f52770..49b9797b2 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 @@ -136,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/ChatCompletionServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/chat/ChatCompletionServiceAsyncImpl.kt index 8b6816bb1..260a4619c 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 @@ -278,6 +278,7 @@ internal constructor(private val clientOptions: ClientOptions) : ChatCompletionS .let { ChatCompletionListPageAsync.builder() .service(ChatCompletionServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() 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 f2b8ced35..271a18a6d 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 @@ -74,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/RunServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/evals/RunServiceAsyncImpl.kt index f8489292f..b5d6eb0e9 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 @@ -191,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() 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 9163a7b40..ad4cd7fa2 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 @@ -129,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/JobServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/finetuning/JobServiceAsyncImpl.kt index be6201616..959d5660c 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 @@ -199,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() @@ -272,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() 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 f0c1b455f..0d3bf5c02 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 @@ -99,6 +99,7 @@ class PermissionServiceAsyncImpl internal constructor(private val clientOptions: .let { PermissionCreatePageAsync.builder() .service(PermissionServiceAsyncImpl(clientOptions)) + .streamHandlerExecutor(clientOptions.streamHandlerExecutor) .params(params) .response(it) .build() 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 c596bf4d7..e4f742351 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 @@ -74,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/responses/InputItemServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/responses/InputItemServiceAsyncImpl.kt index 180766980..0dbd0e042 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 @@ -73,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/vectorstores/FileBatchServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileBatchServiceAsyncImpl.kt index f349afb5e..1d8d80865 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 @@ -228,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/FileServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/vectorstores/FileServiceAsyncImpl.kt index 18fe38e5a..9780e9b6e 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 @@ -237,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() @@ -324,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/test/kotlin/com/openai/core/AutoPagerAsyncTest.kt b/openai-java-core/src/test/kotlin/com/openai/core/AutoPagerAsyncTest.kt new file mode 100644 index 000000000..e9e9137e6 --- /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 000000000..17e586320 --- /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") + } +} From 6cdb6717e047e96f9f4186bec3beca0744f27a3a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 00:05:38 +0000 Subject: [PATCH 07/17] refactor(client)!: change precision of some numeric types --- .../transcriptions/TranscriptionSegment.kt | 96 +++++++++---------- .../audio/transcriptions/TranscriptionWord.kt | 32 +++---- .../responses/ResponseFileSearchToolCall.kt | 20 ++-- .../TranscriptionCreateResponseTest.kt | 28 +++--- .../TranscriptionSegmentTest.kt | 36 +++---- .../TranscriptionVerboseTest.kt | 42 ++++---- .../transcriptions/TranscriptionWordTest.kt | 10 +- .../TranslationCreateResponseTest.kt | 24 ++--- .../translations/TranslationVerboseTest.kt | 36 +++---- .../ResponseFileSearchToolCallTest.kt | 6 +- .../models/responses/ResponseInputItemTest.kt | 4 +- .../models/responses/ResponseItemTest.kt | 4 +- .../responses/ResponseOutputItemTest.kt | 4 +- 13 files changed, 172 insertions(+), 170 deletions(-) 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 ffd5f56d7..c2de7f04c 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 e17f25058..2ebd3c150 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/responses/ResponseFileSearchToolCall.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFileSearchToolCall.kt index 35019c26a..1e0389b6e 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/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/audio/transcriptions/TranscriptionCreateResponseTest.kt index 0a7c3f77f..8f64d432c 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 127ee6b96..85eef2387 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 6c3ceef9d..8f0a543fd 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 0bf1311aa..518a754ec 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 53fc2b4f1..098a9b842 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 fc5baf5d4..7b318663e 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/responses/ResponseFileSearchToolCallTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFileSearchToolCallTest.kt index 50752fd39..414ef927c 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 4ca8296c1..875e90e4f 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 2e4bfed33..2f3896e89 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 763f097af..66edbe7df 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() ) From 1da6c92fc964837aec19a3688ffb9a1089b3d91c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 20:55:45 -0400 Subject: [PATCH 08/17] chore(internal): fix custom code --- .../openai/example/AssistantAsyncExample.java | 22 ++++++++----------- .../openai/example/ModelListAsyncExample.java | 9 ++------ 2 files changed, 11 insertions(+), 20 deletions(-) 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 93a80bb6e..78f882317 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 29376e7bd..3233ef08c 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(); } } From f3e1c6494df23f6c1a70987ca7ee79c1965c933f Mon Sep 17 00:00:00 2001 From: D Gardner Date: Fri, 2 May 2025 15:24:11 +0100 Subject: [PATCH 09/17] structured-outputs: updates and more unit tests. --- openai-java-core/build.gradle.kts | 2 + .../com/openai/core/StructuredOutputs.kt | 67 + .../completions/ChatCompletionCreateParams.kt | 8 + .../completions/StructuredChatCompletion.kt | 169 ++ .../StructuredChatCompletionCreateParams.kt | 744 +++++++++ .../StructuredChatCompletionMessage.kt | 92 ++ .../blocking/chat/ChatCompletionService.kt | 12 + .../openai/core/JsonSchemaValidatorTest.kt | 1403 +++++++++++++++++ .../ChatCompletionCreateParamsTest.kt | 32 + ...tructuredChatCompletionCreateParamsTest.kt | 499 ++++++ .../StructuredChatCompletionMessageTest.kt | 141 ++ .../StructuredChatCompletionTest.kt | 405 +++++ .../StructuredOutputsClassExample.java | 73 + 13 files changed, 3647 insertions(+) create mode 100644 openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletion.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletionCreateParams.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletionMessage.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/core/JsonSchemaValidatorTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/chat/completions/StructuredChatCompletionCreateParamsTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/chat/completions/StructuredChatCompletionMessageTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/chat/completions/StructuredChatCompletionTest.kt create mode 100644 openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java diff --git a/openai-java-core/build.gradle.kts b/openai-java-core/build.gradle.kts index 08a91e0dc..894f0e234 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/core/StructuredOutputs.kt b/openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt new file mode 100644 index 000000000..7f18d237f --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt @@ -0,0 +1,67 @@ +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 + +// 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() + +fun fromClass(type: Class) = + ResponseFormatJsonSchema.builder() + .jsonSchema( + ResponseFormatJsonSchema.JsonSchema.builder() + .name("json-schema-from-${type.simpleName}") + .schema(JsonValue.from(extractSchema(type))) + .build() + ) + .build() + +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) +} + +fun fromJson(json: String, type: Class): T = + try { + MAPPER.readValue(json, type) + } 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/chat/completions/ChatCompletionCreateParams.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParams.kt index a3281dc66..cb3459feb 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 @@ -1297,6 +1297,14 @@ private constructor( body.responseFormat(jsonObject) } + /** + * Sets the class that defines the structured outputs response format. This changes the + * builder to a type-safe [StructuredChatCompletionCreateParams.Builder] that will build a + * [StructuredChatCompletionCreateParams] instance when `build()` is called. + */ + fun responseFormat(responseFormat: Class) = + StructuredChatCompletionCreateParams.builder().wrap(responseFormat, this) + /** * 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/StructuredChatCompletion.kt b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletion.kt new file mode 100644 index 000000000..6ca931a55 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletion.kt @@ -0,0 +1,169 @@ +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 + +class StructuredChatCompletion( + val responseFormat: Class, + val chatCompletion: ChatCompletion, +) { + /** @see ChatCompletion.id */ + fun id(): String = chatCompletion.id() + + private val choices by lazy { + chatCompletion._choices().map { choices -> choices.map { Choice(responseFormat, it) } } + } + + /** @see ChatCompletion.choices */ + fun choices(): List> = choices.getRequired("choices") + + /** @see ChatCompletion.created */ + fun created(): Long = chatCompletion.created() + + /** @see ChatCompletion.model */ + fun model(): String = chatCompletion.model() + + /** @see ChatCompletion._object_ */ + fun _object_(): JsonValue = chatCompletion._object_() + + /** @see ChatCompletion.serviceTier */ + fun serviceTier(): Optional = chatCompletion.serviceTier() + + /** @see ChatCompletion.systemFingerprint */ + fun systemFingerprint(): Optional = chatCompletion.systemFingerprint() + + /** @see ChatCompletion.usage */ + fun usage(): Optional = chatCompletion.usage() + + /** @see ChatCompletion._id */ + fun _id(): JsonField = chatCompletion._id() + + /** @see ChatCompletion._choices */ + fun _choices(): JsonField>> = choices + + /** @see ChatCompletion._created */ + fun _created(): JsonField = chatCompletion._created() + + /** @see ChatCompletion._model */ + fun _model(): JsonField = chatCompletion._model() + + /** @see ChatCompletion._serviceTier */ + fun _serviceTier(): JsonField = chatCompletion._serviceTier() + + /** @see ChatCompletion._systemFingerprint */ + fun _systemFingerprint(): JsonField = chatCompletion._systemFingerprint() + + /** @see ChatCompletion._usage */ + fun _usage(): JsonField = chatCompletion._usage() + + /** @see ChatCompletion._additionalProperties */ + fun _additionalProperties(): Map = chatCompletion._additionalProperties() + + class Choice + internal constructor( + internal val responseFormat: Class, + internal val choice: ChatCompletion.Choice, + ) { + /** @see ChatCompletion.Choice.finishReason */ + fun finishReason(): FinishReason = choice.finishReason() + + /** @see ChatCompletion.Choice.index */ + fun index(): Long = choice.index() + + /** @see ChatCompletion.Choice.logprobs */ + fun logprobs(): Optional = choice.logprobs() + + /** @see ChatCompletion.Choice._finishReason */ + fun _finishReason(): JsonField = choice._finishReason() + + private val message by lazy { + choice._message().map { StructuredChatCompletionMessage(responseFormat, it) } + } + + /** @see ChatCompletion.Choice.message */ + fun message(): StructuredChatCompletionMessage = message.getRequired("message") + + /** @see ChatCompletion.Choice._index */ + fun _index(): JsonField = choice._index() + + /** @see ChatCompletion.Choice._logprobs */ + fun _logprobs(): JsonField = choice._logprobs() + + /** @see ChatCompletion.Choice._message */ + fun _message(): JsonField> = message + + /** @see ChatCompletion.Choice._additionalProperties */ + fun _additionalProperties(): Map = choice._additionalProperties() + + /** @see ChatCompletion.Choice.validate */ + fun validate(): Choice = apply { + message().validate() + choice.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<*> && + responseFormat == other.responseFormat && + choice == other.choice + } + + private val hashCode: Int by lazy { Objects.hash(responseFormat, choice) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "${javaClass.simpleName}{responseFormat=$responseFormat, choice=$choice}" + } + + /** @see ChatCompletion.validate */ + fun validate() = apply { + choices().forEach { it.validate() } + chatCompletion.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<*> && + responseFormat == other.responseFormat && + chatCompletion == other.chatCompletion + } + + private val hashCode: Int by lazy { Objects.hash(responseFormat, chatCompletion) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "${javaClass.simpleName}{responseFormat=$responseFormat, chatCompletion=$chatCompletion}" +} 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 000000000..ae1ea1be7 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletionCreateParams.kt @@ -0,0 +1,744 @@ +package com.openai.models.chat.completions + +import com.openai.core.JsonField +import com.openai.core.JsonValue +import com.openai.core.checkRequired +import com.openai.core.fromClass +import com.openai.core.http.Headers +import com.openai.core.http.QueryParams +import com.openai.models.ChatModel +import com.openai.models.ReasoningEffort +import java.util.Objects +import java.util.Optional + +class StructuredChatCompletionCreateParams +internal constructor( + val responseFormat: 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 responseFormat: Class? = null + private var paramsBuilder = ChatCompletionCreateParams.builder() + + @JvmSynthetic + internal fun wrap( + responseFormat: Class, + paramsBuilder: ChatCompletionCreateParams.Builder, + ) = apply { + this.responseFormat = responseFormat + this.paramsBuilder = paramsBuilder + // Convert the class to a JSON schema and apply it to the delegate `Builder`. + responseFormat(responseFormat) + } + + /** 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 given class. */ + fun responseFormat(responseFormat: Class) = apply { + this.responseFormat = responseFormat + paramsBuilder.responseFormat(fromClass(responseFormat)) + } + + /** @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("responseFormat", responseFormat), + paramsBuilder.build(), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is StructuredChatCompletionCreateParams<*> && + responseFormat == other.responseFormat && + rawParams == other.rawParams + } + + override fun hashCode(): Int = Objects.hash(responseFormat, rawParams) + + override fun toString() = + "${javaClass.simpleName}{responseFormat=$responseFormat, params=$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 000000000..519596ef7 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletionMessage.kt @@ -0,0 +1,92 @@ +package com.openai.models.chat.completions + +import com.openai.core.JsonField +import com.openai.core.JsonValue +import com.openai.core.fromJson +import com.openai.models.chat.completions.ChatCompletionMessage.FunctionCall +import java.util.Objects +import java.util.Optional + +class StructuredChatCompletionMessage +internal constructor( + val responseFormat: Class, + val chatCompletionMessage: ChatCompletionMessage, +) { + + private val content: JsonField by lazy { + chatCompletionMessage._content().map { fromJson(it, responseFormat) } + } + + /** @see ChatCompletionMessage.content */ + fun content(): Optional = content.getOptional("content") + + /** @see ChatCompletionMessage.refusal */ + fun refusal(): Optional = chatCompletionMessage.refusal() + + /** @see ChatCompletionMessage._role */ + fun _role(): JsonValue = chatCompletionMessage._role() + + /** @see ChatCompletionMessage.annotations */ + fun annotations(): Optional> = + chatCompletionMessage.annotations() + + /** @see ChatCompletionMessage.audio */ + fun audio(): Optional = chatCompletionMessage.audio() + + /** @see ChatCompletionMessage.functionCall */ + @Deprecated("deprecated") + fun functionCall(): Optional = chatCompletionMessage.functionCall() + + /** @see ChatCompletionMessage.toolCalls */ + fun toolCalls(): Optional> = + chatCompletionMessage.toolCalls() + + /** @see ChatCompletionMessage._content */ + fun _content(): JsonField = content + + /** @see ChatCompletionMessage._refusal */ + fun _refusal(): JsonField = chatCompletionMessage._refusal() + + /** @see ChatCompletionMessage._annotations */ + fun _annotations(): JsonField> = + chatCompletionMessage._annotations() + + /** @see ChatCompletionMessage._audio */ + fun _audio(): JsonField = chatCompletionMessage._audio() + + /** @see ChatCompletionMessage._functionCall */ + @Deprecated("deprecated") + fun _functionCall(): JsonField = chatCompletionMessage._functionCall() + + /** @see ChatCompletionMessage._toolCalls */ + fun _toolCalls(): JsonField> = + chatCompletionMessage._toolCalls() + + /** @see ChatCompletionMessage._additionalProperties */ + fun _additionalProperties(): Map = + chatCompletionMessage._additionalProperties() + + /** @see ChatCompletionMessage.validate */ + // `content()` is not included in the validation by the delegate method, so just call it. + fun validate(): ChatCompletionMessage = chatCompletionMessage.validate() + + /** @see ChatCompletionMessage.isValid */ + fun isValid(): Boolean = chatCompletionMessage.isValid() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is StructuredChatCompletionMessage<*> && + responseFormat == other.responseFormat && + chatCompletionMessage == other.chatCompletionMessage + } + + private val hashCode: Int by lazy { Objects.hash(responseFormat, chatCompletionMessage) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "${javaClass.simpleName}{responseFormat=$responseFormat, chatCompletionMessage=$chatCompletionMessage}" +} 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 8febcf124..28818c45c 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,16 @@ interface ChatCompletionService { requestOptions: RequestOptions = RequestOptions.none(), ): ChatCompletion + /** @see create */ + fun create( + params: StructuredChatCompletionCreateParams + ): StructuredChatCompletion = + StructuredChatCompletion( + params.responseFormat, + // Normal, non-generic create method call via `ChatCompletionCreateParams`. + create(params.rawParams), + ) + /** * **Starting a new project?** We recommend trying * [Responses](https://platform.openai.com/docs/api-reference/responses) to take advantage of diff --git a/openai-java-core/src/test/kotlin/com/openai/core/JsonSchemaValidatorTest.kt b/openai-java-core/src/test/kotlin/com/openai/core/JsonSchemaValidatorTest.kt new file mode 100644 index 000000000..31768c04b --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/core/JsonSchemaValidatorTest.kt @@ -0,0 +1,1403 @@ +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 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.api.extension.AfterTestExecutionCallback +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.RegisterExtension + +/** Tests the [JsonSchemaValidator] and, in passing, tests the [extractSchema] function. */ +internal class JsonSchemaValidatorTest { + 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 + } + + /** + * 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) + + // FIXME: 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 is expected: + // { + // "$schema" : "https://json-schema.org/draft/2020-12/schema", + // "type" : "array", + // "items" : { + // "type" : "string" + // } + // } + // It might be presumed that type erasure is the cause of the missing field. However, the + // `schemaTest_listFieldSchema` method (below) seems to be able to produce the expected + // `"items"` object when it is defined as a class property, so, well ... huh? + assertThat(validator.isValid()).isTrue + } + + @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) + + // TODO: Decide if this is the expected behavior, i.e., that it is OK for an "object" schema + // to have no "properties". + 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 + } + + private fun parseJson(schemaString: String) = ObjectMapper().readTree(schemaString) +} 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 e47aebd6b..fb52ffc6e 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.responseFormat).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 000000000..4abd66b67 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/StructuredChatCompletionCreateParamsTest.kt @@ -0,0 +1,499 @@ +package com.openai.models.chat.completions + +import com.openai.core.fromClass +import com.openai.core.http.Headers +import com.openai.core.http.QueryParams +import com.openai.models.ChatModel +import com.openai.models.FunctionDefinition +import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.JSON_FIELD +import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.JSON_VALUE +import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.MESSAGE +import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.OPTIONAL +import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.STRING +import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.X +import java.lang.reflect.Method +import kotlin.collections.plus +import kotlin.reflect.full.declaredFunctions +import kotlin.reflect.jvm.javaMethod +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.fail +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 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`. + */ + private 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." } + } + + // Using `if` and `fail`, so the compiler knows the code will not continue and can infer + // that `delegationMethod` is not null. It cannot do this for `assertThat...isNotNull`. + if (method == null) { + fail { "Function $testCase cannot be found in $target." } + } + + return method + } + + 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 + } + + private val NULLABLE = null + private const val BOOLEAN: Boolean = true + private val NULLABLE_BOOLEAN: Boolean? = null + private const val LONG: Long = 42L + private val NULLABLE_LONG: Long? = null + private const val DOUBLE: Double = 42.0 + private val NULLABLE_DOUBLE: Double? = null + private val LIST = listOf(STRING) + private val SET = setOf(STRING) + private val MAP = mapOf(STRING to STRING) + + private val CHAT_MODEL = ChatModel.GPT_4 + + 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() + + // Want `vararg`, so cannot use `data class`. Need a custom `toString`, anyway. + class DelegationWriteTestCase(val functionName: String, 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" + }})" + } + + // The list order follows the declaration order in `ChatCompletionCreateParams.Builder` for + // easier maintenance. + @JvmStatic + 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). + val mockBuilderDelegate: ChatCompletionCreateParams.Builder = + mock(ChatCompletionCreateParams.Builder::class.java) + val builderDelegator = + StructuredChatCompletionCreateParams.builder().inject(mockBuilderDelegate) + + @Test + fun allBuilderDelegateFunctionsExistInDelegator() { + // The delegator class does not implement the various `responseFormat` functions of the + // delegate class. + StructuredChatCompletionTest.checkAllDelegation( + ChatCompletionCreateParams.Builder::class, + StructuredChatCompletionCreateParams.Builder::class, + "responseFormat", + ) + } + + @Test + fun allBuilderDelegatorFunctionsExistInDelegate() { + // The delegator implements a different `responseFormat` function from those overloads in + // the delegate class. + StructuredChatCompletionTest.checkAllDelegation( + StructuredChatCompletionCreateParams.Builder::class, + ChatCompletionCreateParams.Builder::class, + "responseFormat", + ) + } + + @Test + fun allBuilderDelegatorFunctionsAreTested() { + // 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 are (at this + // time) 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 exceptionalTestedFns = listOf("responseFormat") + val testedFns = + (builderDelegationTestCases().map { it.functionName } + exceptionalTestedFns) + .toMutableList() + val nonDelegatingFns = listOf("build", "wrap", "inject") + + val delegatorFns = + StructuredChatCompletionCreateParams.Builder::class.declaredFunctions.toMutableList() + + // Making concurrent modifications to the list, so using an `Iterator`. + val i = delegatorFns.iterator() + + while (i.hasNext()) { + val functionName = i.next().name + + if (functionName in testedFns) { + testedFns.remove(functionName) + i.remove() + } + if (functionName in nonDelegatingFns) { + i.remove() + } + } + + // If there are function names remaining in `delegatorFns`, then there are tests missing. + // Only report the names of the functions not tested: parameters are not matched, so any + // signatures could be misleading. + assertThat(delegatorFns) + .describedAs { + "Delegation is not tested for functions ${delegatorFns.map { it.name }}." + } + .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() + } + + @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", fromClass(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 000000000..347788a35 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/StructuredChatCompletionMessageTest.kt @@ -0,0 +1,141 @@ +package com.openai.models.chat.completions + +import com.openai.core.JsonField +import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.DelegationReadTestCase +import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.JSON_FIELD +import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.JSON_VALUE +import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.MESSAGE +import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.OPTIONAL +import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.X +import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.checkOneDelegationRead +import java.util.Optional +import kotlin.reflect.full.declaredFunctions +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 { + // The list order follows the declaration order in `StructuredChatCompletionMessage` for + // easier maintenance. See `StructuredChatCompletionTest` for details on the values used. + @JvmStatic + 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). + val mockDelegate: ChatCompletionMessage = mock(ChatCompletionMessage::class.java) + val delegator = StructuredChatCompletionMessage(X::class.java, mockDelegate) + + @Test + fun allDelegateFunctionsExistInDelegator() { + StructuredChatCompletionTest.checkAllDelegation( + ChatCompletionMessage::class, + StructuredChatCompletionMessage::class, + "toBuilder", + "toParam", + ) + } + + @Test + fun allDelegatorFunctionsExistInDelegate() { + StructuredChatCompletionTest.checkAllDelegation( + StructuredChatCompletionMessage::class, + ChatCompletionMessage::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. + val exceptionalTestedFns = setOf("content", "_content") + val testedFns = delegationTestCases().map { it.functionName }.toSet() + exceptionalTestedFns + // A few delegator functions do not delegate, so no test function is necessary. + val nonDelegatingFns = listOf("equals", "hashCode", "toString") + + val delegatorFunctions = StructuredChatCompletionMessage::class.declaredFunctions + + for (delegatorFunction in delegatorFunctions) { + assertThat( + delegatorFunction.name in testedFns || + delegatorFunction.name in nonDelegatingFns + ) + .describedAs("Delegation is not tested for function '${delegatorFunction.name}.") + .isTrue + } + } + + @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 000000000..af380bbf6 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/StructuredChatCompletionTest.kt @@ -0,0 +1,405 @@ +package com.openai.models.chat.completions + +import com.openai.core.JsonField +import com.openai.core.JsonValue +import com.openai.errors.OpenAIInvalidDataException +import java.util.Optional +import kotlin.reflect.KClass +import kotlin.reflect.KVisibility +import kotlin.reflect.full.declaredFunctions +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 { + internal fun checkAllDelegation( + delegateClass: KClass<*>, + delegatorClass: KClass<*>, + vararg exceptFunctionNames: String, + ) { + assertThat(delegateClass != delegatorClass) + .describedAs { "Delegate and delegator classes should not be the same." } + .isTrue + + val delegateFunctions = delegateClass.declaredFunctions + + for (delegateFunction in delegateFunctions) { + if (delegateFunction.visibility != KVisibility.PUBLIC) { + // Non-public methods are just implementation details of each class. + continue + } + + if (delegateFunction.name in exceptFunctionNames) { + // Ignore functions that are known exceptions (e.g., `toBuilder`). + 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 delegatorFunction = + delegatorClass.declaredFunctions.find { + it.name == delegateFunction.name && + it.parameters.drop(1).map { it.type } == + delegateFunction.parameters.drop(1).map { it.type } + } + + assertThat(delegatorFunction != null) + .describedAs { + "Function $delegateFunction is not found in ${delegatorClass.simpleName}." + } + .isTrue + } + } + + 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) + } + + // 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 with the test. Constants have been provided for this purpose. + internal const val STRING = "a-string" + + internal val OPTIONAL = Optional.of(STRING) + internal val JSON_FIELD = JsonField.of(STRING) + internal val JSON_VALUE = JsonValue.from(STRING) + internal 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() + + data class DelegationReadTestCase(val functionName: String, val expectedValue: Any) + + // The list order follows the declaration order in `StructuredChatCompletionMessage` for + // easier maintenance. + @JvmStatic + fun delegationTestCases() = + listOf( + DelegationReadTestCase("id", STRING), + // `choices()` is a special case and has its own test function. + DelegationReadTestCase("created", 123L), + 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 + fun choiceDelegationTestCases() = + listOf( + DelegationReadTestCase("finishReason", FINISH_REASON), + DelegationReadTestCase("index", 123L), + 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. + ) + + /** 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() + } + } + + // 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). + val mockDelegate: ChatCompletion = mock(ChatCompletion::class.java) + val delegator = StructuredChatCompletion(X::class.java, mockDelegate) + + val mockChoiceDelegate: ChatCompletion.Choice = mock(ChatCompletion.Choice::class.java) + val choiceDelegator = StructuredChatCompletion.Choice(X::class.java, mockChoiceDelegate) + + @Test + fun allChatCompletionDelegateFunctionsExistInDelegator() { + checkAllDelegation(ChatCompletion::class, StructuredChatCompletion::class, "toBuilder") + } + + @Test + fun allChatCompletionDelegatorFunctionsExistInDelegate() { + checkAllDelegation(StructuredChatCompletion::class, ChatCompletion::class) + } + + @Test + fun allChoiceDelegateFunctionsExistInDelegator() { + checkAllDelegation( + ChatCompletion.Choice::class, + StructuredChatCompletion.Choice::class, + "toBuilder", + ) + } + + @Test + fun allChoiceDelegatorFunctionsExistInDelegate() { + checkAllDelegation(StructuredChatCompletion.Choice::class, ChatCompletion.Choice::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. + val exceptionalTestedFns = setOf("choices", "_choices", "validate", "isValid") + val testedFns = delegationTestCases().map { it.functionName }.toSet() + exceptionalTestedFns + // A few delegator functions do not delegate, so no test function is necessary. + val nonDelegatingFns = listOf("equals", "hashCode", "toString") + + val delegatorFunctions = StructuredChatCompletion::class.declaredFunctions + + for (delegatorFunction in delegatorFunctions) { + assertThat( + delegatorFunction.name in testedFns || + delegatorFunction.name in nonDelegatingFns + ) + .describedAs("Delegation is not tested for function '${delegatorFunction.name}.") + .isTrue + } + } + + @Test + fun allChoiceDelegatorFunctionsAreTested() { + // There are exceptional test cases for some functions. Most other functions are part of the + // list of those using the parameterized test. + val exceptionalTestedFns = setOf("message", "_message", "validate", "isValid") + val testedFns = + choiceDelegationTestCases().map { it.functionName }.toSet() + exceptionalTestedFns + // A few delegator functions do not delegate, so no test function is necessary. + val nonDelegatingFns = listOf("equals", "hashCode", "toString") + + val delegatorFunctions = StructuredChatCompletion.Choice::class.declaredFunctions + + for (delegatorFunction in delegatorFunctions) { + assertThat( + delegatorFunction.name in testedFns || + delegatorFunction.name in nonDelegatingFns + ) + .describedAs( + "Delegation is not tested for function 'Choice.${delegatorFunction.name}." + ) + .isTrue + } + } + + @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].choice).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].choice).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.chatCompletionMessage).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").chatCompletionMessage).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-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java b/openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java new file mode 100644 index 000000000..bcc46a80f --- /dev/null +++ b/openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java @@ -0,0 +1,73 @@ +package com.openai.example; + +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.chat.completions.ChatCompletionCreateParams; +import com.openai.models.chat.completions.StructuredChatCompletionCreateParams; +import java.util.List; + +public final class StructuredOutputsClassExample { + + public static class Person { + public String firstName; + public String surname; + + @JsonPropertyDescription("The date of birth of the person.") + public String dateOfBirth; + + @Override + public String toString() { + return "Person{firstName=" + firstName + ", surname=" + surname + ", dateOfBirth=" + dateOfBirth + '}'; + } + } + + public static class Laureate { + public Person person; + public String majorAchievement; + public int yearWon; + + @JsonPropertyDescription("The share of the prize money won by the Nobel Laureate.") + public double prizeMoney; + + @Override + public String toString() { + return "Laureate{person=" + + person + ", majorAchievement=" + + majorAchievement + ", yearWon=" + + yearWon + ", prizeMoney=" + + prizeMoney + '}'; + } + } + + public static class Laureates { + @JsonPropertyDescription("A list of winners of a Nobel Prize.") + public List laureates; + + @Override + public String toString() { + return "Laureates{laureates=" + laureates + '}'; + } + } + + private StructuredOutputsClassExample() {} + + 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(); + + StructuredChatCompletionCreateParams createParams = ChatCompletionCreateParams.builder() + .model(ChatModel.GPT_4O_MINI) + .maxCompletionTokens(2048) + .responseFormat(Laureates.class) + .addUserMessage("List some winners of the Nobel Prize in Physics since 2000.") + .build(); + + client.chat().completions().create(createParams).choices().stream() + .flatMap(choice -> choice.message().content().stream()) + .forEach(System.out::println); + } +} From bc3c47926e7c598d48e1eb85fcfacf85ccfb4c9a Mon Sep 17 00:00:00 2001 From: D Gardner Date: Fri, 2 May 2025 16:40:52 +0100 Subject: [PATCH 10/17] structured-outputs: repair after bad merge. --- .../com/openai/core/JsonSchemaValidator.kt | 670 ++++++++++++++++++ .../openai/core/JsonSchemaValidatorTest.kt | 3 +- 2 files changed, 672 insertions(+), 1 deletion(-) create mode 100644 openai-java-core/src/main/kotlin/com/openai/core/JsonSchemaValidator.kt 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 000000000..6af409290 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/core/JsonSchemaValidator.kt @@ -0,0 +1,670 @@ +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 { + if (isValidationComplete) { + throw IllegalStateException("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/test/kotlin/com/openai/core/JsonSchemaValidatorTest.kt b/openai-java-core/src/test/kotlin/com/openai/core/JsonSchemaValidatorTest.kt index 31768c04b..ccbc3926d 100644 --- a/openai-java-core/src/test/kotlin/com/openai/core/JsonSchemaValidatorTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/core/JsonSchemaValidatorTest.kt @@ -82,7 +82,8 @@ internal class JsonSchemaValidatorTest { assertThat(validator.isValid()).isTrue } - @Test + // FIXME: Disabled test until issues (noted below) are resolved. + // @Test fun schemaTest_minimalListSchema() { val s: List = listOf() From 5ab252cfa07550be1920d67cd01fd87e7d7170db Mon Sep 17 00:00:00 2001 From: D Gardner Date: Mon, 5 May 2025 16:27:46 +0100 Subject: [PATCH 11/17] structured-outputs: local validation, unit tests and documentation --- README.md | 166 +++++++++++++++++- .../com/openai/core/StructuredOutputs.kt | 28 ++- .../completions/ChatCompletionCreateParams.kt | 23 ++- .../StructuredChatCompletionCreateParams.kt | 14 +- ...idatorTest.kt => StructuredOutputsTest.kt} | 103 ++++++++++- .../StructuredOutputsClassExample.java | 10 +- 6 files changed, 314 insertions(+), 30 deletions(-) rename openai-java-core/src/test/kotlin/com/openai/core/{JsonSchemaValidatorTest.kt => StructuredOutputsTest.kt} (92%) diff --git a/README.md b/README.md index 39125ce8b..2b8cb8813 100644 --- a/README.md +++ b/README.md @@ -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,166 @@ 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 response will then +automatically convert the generated JSON content to an instance of that Java class. + +Java classes can contain fields declared to be instances of other classes and can use collections: + +```java +class Person { + public String name; + public int yearOfBirth; +} + +class Book { + public String title; + public Person author; + public int yearPublished; +} + +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 six famous nineteenth 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 yearPublished; + public Optional isbn; +} +``` + +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 model. +- **Disabling Local Validation**: If you encounter compatibility issues and wish to bypass local +validation, you can disable it by passing `false` to the `responseFormat(Class, boolean)` method +when building the parameters. (The default value for this parameter is `true`.) + +```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 six famous nineteenth century novels.") + .model(ChatModel.GPT_4_1) + .responseFormat(BookList.class, false) // Disable local validation. + .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. + +### 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 yearOfBirth; +} + +@JsonClassDescription("The details of one published book") +class Book { + public String title; + public Person author; + public int yearPublished; + @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. @@ -652,7 +812,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 @@ -665,7 +825,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 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 index 7f18d237f..ba828d9ab 100644 --- a/openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt +++ b/openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt @@ -23,17 +23,37 @@ private val MAPPER = .addModule(JavaTimeModule()) .build() -fun fromClass(type: Class) = - ResponseFormatJsonSchema.builder() +internal fun fromClass( + type: Class, + localValidation: Boolean = true, +): ResponseFormatJsonSchema { + val schema = extractSchema(type) + + if (localValidation) { + val validator = JsonSchemaValidator.create().validate(schema) + + if (!validator.isValid()) { + throw IllegalArgumentException( + "Local validation failed for JSON schema derived from $type:\n" + + validator.errors().joinToString("\n") { " - $it" } + ) + } + } + + return ResponseFormatJsonSchema.builder() .jsonSchema( ResponseFormatJsonSchema.JsonSchema.builder() .name("json-schema-from-${type.simpleName}") - .schema(JsonValue.from(extractSchema(type))) + .schema(JsonValue.from(schema)) .build() ) .build() +} internal fun extractSchema(type: Class): JsonNode { + // 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. val configBuilder = SchemaGeneratorConfigBuilder( com.github.victools.jsonschema.generator.SchemaVersion.DRAFT_2020_12, @@ -56,7 +76,7 @@ internal fun extractSchema(type: Class): JsonNode { return SchemaGenerator(configBuilder.build()).generateSchema(type) } -fun fromJson(json: String, type: Class): T = +internal fun fromJson(json: String, type: Class): T = try { MAPPER.readValue(json, type) } catch (e: Exception) { 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 cb3459feb..0fe22b2b7 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 @@ -1298,12 +1298,23 @@ private constructor( } /** - * Sets the class that defines the structured outputs response format. This changes the - * builder to a type-safe [StructuredChatCompletionCreateParams.Builder] that will build a - * [StructuredChatCompletionCreateParams] instance when `build()` is called. - */ - fun responseFormat(responseFormat: Class) = - StructuredChatCompletionCreateParams.builder().wrap(responseFormat, this) + * Sets 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 responseFormat A class from which a JSON schema will be derived to define the + * response format. + * @param localValidation `true` (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 `false` to disable + * local 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(responseFormat: Class, localValidation: Boolean = true) = + StructuredChatCompletionCreateParams.builder() + .wrap(responseFormat, this, localValidation) /** * This feature is in Beta. If specified, our system will make a best effort to sample 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 index ae1ea1be7..14194ac9f 100644 --- 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 @@ -33,11 +33,12 @@ internal constructor( internal fun wrap( responseFormat: Class, paramsBuilder: ChatCompletionCreateParams.Builder, + localValidation: Boolean, ) = apply { this.responseFormat = responseFormat this.paramsBuilder = paramsBuilder // Convert the class to a JSON schema and apply it to the delegate `Builder`. - responseFormat(responseFormat) + responseFormat(responseFormat, localValidation) } /** Injects a given `ChatCompletionCreateParams.Builder`. For use only when testing. */ @@ -389,10 +390,15 @@ internal constructor( paramsBuilder.reasoningEffort(reasoningEffort) } - /** Sets the response format to a JSON schema derived from the given class. */ - fun responseFormat(responseFormat: Class) = apply { + /** + * Sets the response format to a JSON schema derived from the structure of the given class. + * + * @see ChatCompletionCreateParams.Builder.responseFormat + */ + @JvmOverloads + fun responseFormat(responseFormat: Class, localValidation: Boolean = true) = apply { this.responseFormat = responseFormat - paramsBuilder.responseFormat(fromClass(responseFormat)) + paramsBuilder.responseFormat(fromClass(responseFormat, localValidation)) } /** @see ChatCompletionCreateParams.Builder.seed */ diff --git a/openai-java-core/src/test/kotlin/com/openai/core/JsonSchemaValidatorTest.kt b/openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTest.kt similarity index 92% rename from openai-java-core/src/test/kotlin/com/openai/core/JsonSchemaValidatorTest.kt rename to openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTest.kt index ccbc3926d..88696e552 100644 --- a/openai-java-core/src/test/kotlin/com/openai/core/JsonSchemaValidatorTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTest.kt @@ -7,16 +7,18 @@ 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 the [JsonSchemaValidator] and, in passing, tests the [extractSchema] function. */ -internal class JsonSchemaValidatorTest { +/** 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" @@ -28,6 +30,8 @@ internal class JsonSchemaValidatorTest { * print them only for failed tests. */ private const val VERBOSE_MODE = false + + private fun parseJson(schemaString: String) = ObjectMapper().readTree(schemaString) } /** @@ -82,7 +86,7 @@ internal class JsonSchemaValidatorTest { assertThat(validator.isValid()).isTrue } - // FIXME: Disabled test until issues (noted below) are resolved. + // TODO: Disabled test until issues (noted below) are resolved. // @Test fun schemaTest_minimalListSchema() { val s: List = listOf() @@ -90,7 +94,7 @@ internal class JsonSchemaValidatorTest { schema = extractSchema(s.javaClass) validator.validate(schema) - // FIXME: Currently, the generated schema looks like this: + // TODO: Currently, the generated schema looks like this: // { // "$schema" : "https://json-schema.org/draft/2020-12/schema", // "type" : "array", @@ -1400,5 +1404,94 @@ internal class JsonSchemaValidatorTest { assertThat(validator.isValid()).isTrue } - private fun parseJson(schemaString: String) = ObjectMapper().readTree(schemaString) + @Test + fun fromJsonSuccess() { + @Suppress("unused") class X(val s: String) + + val x = fromJson("{\"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 { fromJson("{\"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 { fromJson("{\"truncated", X::class.java) } + .isExactlyInstanceOf(OpenAIInvalidDataException::class.java) + .hasMessage("Error parsing JSON: {\"truncated") + } + + @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 { fromClass(X::class.java, false) } + } + + @Test + fun fromClassSuccessWithValidation() { + @Suppress("unused") class X(val s: String) + + assertThatNoException().isThrownBy { fromClass(X::class.java, true) } + } + + @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 { fromClass(Z::class.java, true) } + .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) + + assertThatThrownBy { fromClass(Z::class.java) } // Use default for `localValidation` flag. + .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-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java b/openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java index bcc46a80f..30188fa56 100644 --- a/openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java +++ b/openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java @@ -13,8 +13,6 @@ public final class StructuredOutputsClassExample { public static class Person { public String firstName; public String surname; - - @JsonPropertyDescription("The date of birth of the person.") public String dateOfBirth; @Override @@ -44,11 +42,6 @@ public String toString() { public static class Laureates { @JsonPropertyDescription("A list of winners of a Nobel Prize.") public List laureates; - - @Override - public String toString() { - return "Laureates{laureates=" + laureates + '}'; - } } private StructuredOutputsClassExample() {} @@ -63,11 +56,12 @@ public static void main(String[] args) { .model(ChatModel.GPT_4O_MINI) .maxCompletionTokens(2048) .responseFormat(Laureates.class) - .addUserMessage("List some winners of the Nobel Prize in Physics since 2000.") + .addUserMessage("List five winners of the Nobel Prize in Physics.") .build(); client.chat().completions().create(createParams).choices().stream() .flatMap(choice -> choice.message().content().stream()) + .flatMap(laureates -> laureates.laureates.stream()) .forEach(System.out::println); } } From 65ddf35574aac378de0bc1177dae3dd2cb197411 Mon Sep 17 00:00:00 2001 From: D Gardner Date: Tue, 6 May 2025 18:31:44 +0100 Subject: [PATCH 12/17] structured-outputs: changes from code review --- README.md | 48 ++++++++++++----- .../openai/core/JsonSchemaLocalValidation.kt | 19 +++++++ .../com/openai/core/JsonSchemaValidator.kt | 4 +- .../com/openai/core/StructuredOutputs.kt | 17 ++++--- .../completions/ChatCompletionCreateParams.kt | 16 ++++-- .../completions/StructuredChatCompletion.kt | 15 ++++-- .../StructuredChatCompletionCreateParams.kt | 19 +++++-- .../StructuredChatCompletionMessage.kt | 12 ++++- .../blocking/chat/ChatCompletionService.kt | 12 +++-- .../com/openai/core/StructuredOutputsTest.kt | 35 ++++++++----- .../StructuredOutputsClassExample.java | 51 ++++++++++--------- 11 files changed, 170 insertions(+), 78 deletions(-) create mode 100644 openai-java-core/src/main/kotlin/com/openai/core/JsonSchemaLocalValidation.kt diff --git a/README.md b/README.md index 2b8cb8813..d7366790e 100644 --- a/README.md +++ b/README.md @@ -343,21 +343,23 @@ is a feature that ensures that the model will always generate responses that adh 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 response will then -automatically convert the generated JSON content to an instance of that Java class. +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 +[`StructuredOutputsClassExample`](openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.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 yearOfBirth; + public int birthYear; } class Book { public String title; public Person author; - public int yearPublished; + public int publicationYear; } class BookList { @@ -375,7 +377,7 @@ import com.openai.models.chat.completions.ChatCompletionCreateParams; import com.openai.models.chat.completions.StructuredChatCompletionCreateParams; StructuredChatCompletionCreateParams params = ChatCompletionCreateParams.builder() - .addUserMessage("List six famous nineteenth century novels.") + .addUserMessage("List some famous late twentieth century novels.") .model(ChatModel.GPT_4_1) .responseFormat(BookList.class) .build(); @@ -403,11 +405,25 @@ import java.util.Optional; class Book { public String title; public Person author; - public int yearPublished; + 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 strings can be +derived from the `BoolList.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.class) + // ... + .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 @@ -435,20 +451,23 @@ well. 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 model. +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 `false` to the `responseFormat(Class, boolean)` method -when building the parameters. (The default value for this parameter is `true`.) +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 six famous nineteenth century novels.") + .addUserMessage("List some famous late twentieth century novels.") .model(ChatModel.GPT_4_1) - .responseFormat(BookList.class, false) // Disable local validation. + .responseFormat(BookList.class, JsonSchemaLocalValidation.NO) .build(); ``` @@ -470,14 +489,17 @@ import com.fasterxml.jackson.annotation.JsonPropertyDescription; class Person { @JsonPropertyDescription("The first name and surname of the person") public String name; - public int yearOfBirth; + 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; - public int yearPublished; + @JsonPropertyDescription("The year in which the book was first published.") + public int publicationYear; @JsonIgnore public String genre; } 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 000000000..9a3ae7995 --- /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 index 6af409290..85c20b43c 100644 --- a/openai-java-core/src/main/kotlin/com/openai/core/JsonSchemaValidator.kt +++ b/openai-java-core/src/main/kotlin/com/openai/core/JsonSchemaValidator.kt @@ -201,9 +201,7 @@ internal class JsonSchemaValidator private constructor() { * each new schema. */ fun validate(rootSchema: JsonNode): JsonSchemaValidator { - if (isValidationComplete) { - throw IllegalStateException("Validation already complete.") - } + check(!isValidationComplete) { "Validation already complete." } isValidationComplete = true validateSchema(rootSchema, ROOT_PATH, ROOT_DEPTH) 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 index ba828d9ab..3c6e7dec4 100644 --- a/openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt +++ b/openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt @@ -23,20 +23,19 @@ private val MAPPER = .addModule(JavaTimeModule()) .build() +@JvmSynthetic internal fun fromClass( type: Class, - localValidation: Boolean = true, + localValidation: JsonSchemaLocalValidation = JsonSchemaLocalValidation.YES, ): ResponseFormatJsonSchema { val schema = extractSchema(type) - if (localValidation) { + if (localValidation == JsonSchemaLocalValidation.YES) { val validator = JsonSchemaValidator.create().validate(schema) - if (!validator.isValid()) { - throw IllegalArgumentException( - "Local validation failed for JSON schema derived from $type:\n" + - validator.errors().joinToString("\n") { " - $it" } - ) + require(validator.isValid()) { + "Local validation failed for JSON schema derived from $type:\n" + + validator.errors().joinToString("\n") { " - $it" } } } @@ -44,12 +43,13 @@ internal fun fromClass( .jsonSchema( ResponseFormatJsonSchema.JsonSchema.builder() .name("json-schema-from-${type.simpleName}") - .schema(JsonValue.from(schema)) + .schema(JsonValue.fromJsonNode(schema)) .build() ) .build() } +@JvmSynthetic internal fun extractSchema(type: Class): JsonNode { // 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 @@ -76,6 +76,7 @@ internal fun extractSchema(type: Class): JsonNode { return SchemaGenerator(configBuilder.build()).generateSchema(type) } +@JvmSynthetic internal fun fromJson(json: String, type: Class): T = try { MAPPER.readValue(json, type) 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 0fe22b2b7..d012ff94a 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 @@ -1304,15 +1305,20 @@ private constructor( * * @param responseFormat A class from which a JSON schema will be derived to define the * response format. - * @param localValidation `true` (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 `false` to disable - * local validation. See the SDK documentation for more details. + * @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(responseFormat: Class, localValidation: Boolean = true) = + fun responseFormat( + responseFormat: Class, + localValidation: JsonSchemaLocalValidation = JsonSchemaLocalValidation.YES, + ) = StructuredChatCompletionCreateParams.builder() .wrap(responseFormat, this, localValidation) 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 index 6ca931a55..62cde6f56 100644 --- 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 @@ -10,9 +10,16 @@ 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( - val responseFormat: Class, - val chatCompletion: ChatCompletion, + @get:JvmName("responseFormat") val responseFormat: Class, + @get:JvmName("chatCompletion") val chatCompletion: ChatCompletion, ) { /** @see ChatCompletion.id */ fun id(): String = chatCompletion.id() @@ -68,8 +75,8 @@ class StructuredChatCompletion( class Choice internal constructor( - internal val responseFormat: Class, - internal val choice: ChatCompletion.Choice, + @get:JvmName("responseFormat") val responseFormat: Class, + @get:JvmName("choice") val choice: ChatCompletion.Choice, ) { /** @see ChatCompletion.Choice.finishReason */ fun finishReason(): FinishReason = choice.finishReason() 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 index 14194ac9f..4f6a3a63c 100644 --- 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 @@ -1,6 +1,7 @@ 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.fromClass @@ -11,9 +12,18 @@ 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 type of the [responseFormat] 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( - val responseFormat: Class, + @get:JvmName("responseFormat") val responseFormat: Class, /** * The raw, underlying chat completion create parameters wrapped by this structured instance of * the parameters. @@ -33,7 +43,7 @@ internal constructor( internal fun wrap( responseFormat: Class, paramsBuilder: ChatCompletionCreateParams.Builder, - localValidation: Boolean, + localValidation: JsonSchemaLocalValidation, ) = apply { this.responseFormat = responseFormat this.paramsBuilder = paramsBuilder @@ -396,7 +406,10 @@ internal constructor( * @see ChatCompletionCreateParams.Builder.responseFormat */ @JvmOverloads - fun responseFormat(responseFormat: Class, localValidation: Boolean = true) = apply { + fun responseFormat( + responseFormat: Class, + localValidation: JsonSchemaLocalValidation = JsonSchemaLocalValidation.YES, + ) = apply { this.responseFormat = responseFormat paramsBuilder.responseFormat(fromClass(responseFormat, localValidation)) } 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 index 519596ef7..b833dd476 100644 --- 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 @@ -7,10 +7,18 @@ 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( - val responseFormat: Class, - val chatCompletionMessage: ChatCompletionMessage, + @get:JvmName("responseFormat") val responseFormat: Class, + @get:JvmName("chatCompletionMessage") val chatCompletionMessage: ChatCompletionMessage, ) { private val content: JsonField by lazy { 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 28818c45c..6985e05ea 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 @@ -58,12 +58,14 @@ interface ChatCompletionService { /** @see create */ fun create( params: StructuredChatCompletionCreateParams + ): StructuredChatCompletion = create(params, RequestOptions.none()) + + /** @see create */ + fun create( + params: StructuredChatCompletionCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), ): StructuredChatCompletion = - StructuredChatCompletion( - params.responseFormat, - // Normal, non-generic create method call via `ChatCompletionCreateParams`. - create(params.rawParams), - ) + StructuredChatCompletion(params.responseFormat, create(params.rawParams, requestOptions)) /** * **Starting a new project?** We recommend trying 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 index 88696e552..6ea7454b3 100644 --- a/openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTest.kt @@ -86,22 +86,24 @@ internal class StructuredOutputsTest { assertThat(validator.isValid()).isTrue } - // TODO: Disabled test until issues (noted below) are resolved. - // @Test + @Test fun schemaTest_minimalListSchema() { val s: List = listOf() schema = extractSchema(s.javaClass) validator.validate(schema) - // TODO: Currently, the generated schema looks like this: + // 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 is expected: + // + // 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", @@ -109,10 +111,15 @@ internal class StructuredOutputsTest { // "type" : "string" // } // } - // It might be presumed that type erasure is the cause of the missing field. However, the - // `schemaTest_listFieldSchema` method (below) seems to be able to produce the expected - // `"items"` object when it is defined as a class property, so, well ... huh? - assertThat(validator.isValid()).isTrue + // + // 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 @@ -1444,14 +1451,18 @@ internal class StructuredOutputsTest { class Y(val x: X) class Z(val y: Y) - assertThatNoException().isThrownBy { fromClass(X::class.java, false) } + assertThatNoException().isThrownBy { + fromClass(X::class.java, JsonSchemaLocalValidation.NO) + } } @Test fun fromClassSuccessWithValidation() { @Suppress("unused") class X(val s: String) - assertThatNoException().isThrownBy { fromClass(X::class.java, true) } + assertThatNoException().isThrownBy { + fromClass(X::class.java, JsonSchemaLocalValidation.YES) + } } @Test @@ -1465,7 +1476,7 @@ internal class StructuredOutputsTest { class Y(val x: X) class Z(val y: Y) - assertThatThrownBy { fromClass(Z::class.java, true) } + assertThatThrownBy { fromClass(Z::class.java, JsonSchemaLocalValidation.YES) } .isExactlyInstanceOf(IllegalArgumentException::class.java) .hasMessage( "Local validation failed for JSON schema derived from ${Z::class.java}:\n" + diff --git a/openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java b/openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java index 30188fa56..3f65a9912 100644 --- a/openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java +++ b/openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java @@ -1,5 +1,6 @@ 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; @@ -11,37 +12,41 @@ public final class StructuredOutputsClassExample { public static class Person { - public String firstName; - public String surname; - public String dateOfBirth; + @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 "Person{firstName=" + firstName + ", surname=" + surname + ", dateOfBirth=" + dateOfBirth + '}'; + return name + " (" + birthYear + '-' + deathYear + ')'; } } - public static class Laureate { - public Person person; - public String majorAchievement; - public int yearWon; + 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; - @JsonPropertyDescription("The share of the prize money won by the Nobel Laureate.") - public double prizeMoney; + @JsonIgnore + public String isbn; @Override public String toString() { - return "Laureate{person=" - + person + ", majorAchievement=" - + majorAchievement + ", yearWon=" - + yearWon + ", prizeMoney=" - + prizeMoney + '}'; + return '"' + title + "\" (" + publicationYear + ") [" + genre + "] by " + author; } } - public static class Laureates { - @JsonPropertyDescription("A list of winners of a Nobel Prize.") - public List laureates; + public static class BookList { + public List books; } private StructuredOutputsClassExample() {} @@ -52,16 +57,16 @@ public static void main(String[] args) { // - The `OPENAI_BASE_URL` and `AZURE_OPENAI_KEY` environment variables OpenAIClient client = OpenAIOkHttpClient.fromEnv(); - StructuredChatCompletionCreateParams createParams = ChatCompletionCreateParams.builder() + StructuredChatCompletionCreateParams createParams = ChatCompletionCreateParams.builder() .model(ChatModel.GPT_4O_MINI) .maxCompletionTokens(2048) - .responseFormat(Laureates.class) - .addUserMessage("List five winners of the Nobel Prize in Physics.") + .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()) - .flatMap(laureates -> laureates.laureates.stream()) - .forEach(System.out::println); + .flatMap(bookList -> bookList.books.stream()) + .forEach(book -> System.out.println(" - " + book)); } } From 118b16c39d89a701ffad8b41ccd1c55dcd368921 Mon Sep 17 00:00:00 2001 From: D Gardner Date: Wed, 7 May 2025 11:30:56 +0100 Subject: [PATCH 13/17] structured-outputs: added 'strict' flag --- .../kotlin/com/openai/core/StructuredOutputs.kt | 3 +++ .../com/openai/core/StructuredOutputsTest.kt | 14 +++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) 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 index 3c6e7dec4..6b1889ff1 100644 --- a/openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt +++ b/openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt @@ -44,6 +44,9 @@ internal fun fromClass( ResponseFormatJsonSchema.JsonSchema.builder() .name("json-schema-from-${type.simpleName}") .schema(JsonValue.fromJsonNode(schema)) + // Ensure the model's output strictly adheres to this JSON schema. This is the + // essential "ON switch" for Structured Outputs. + .strict(true) .build() ) .build() 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 index 6ea7454b3..2c1eb8855 100644 --- a/openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTest.kt @@ -1440,6 +1440,18 @@ internal class StructuredOutputsTest { .hasMessage("Error parsing JSON: {\"truncated") } + @Test + fun fromClassEnablesStrictAdherenceToSchema() { + @Suppress("unused") class X(val s: String) + + val jsonSchema = fromClass(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() { @@ -1452,7 +1464,7 @@ internal class StructuredOutputsTest { class Z(val y: Y) assertThatNoException().isThrownBy { - fromClass(X::class.java, JsonSchemaLocalValidation.NO) + fromClass(Z::class.java, JsonSchemaLocalValidation.NO) } } From 9d5b95655b253429b07085e6be37f6ee40aaf639 Mon Sep 17 00:00:00 2001 From: D Gardner Date: Wed, 14 May 2025 13:50:20 +0100 Subject: [PATCH 14/17] structured-outputs: support for Responses API, review changes --- README.md | 29 +- .../com/openai/core/StructuredOutputs.kt | 74 ++- .../completions/ChatCompletionCreateParams.kt | 13 +- .../completions/StructuredChatCompletion.kt | 74 +-- .../StructuredChatCompletionCreateParams.kt | 34 +- .../StructuredChatCompletionMessage.kt | 49 +- .../models/responses/ResponseCreateParams.kt | 23 + .../models/responses/StructuredResponse.kt | 230 ++++++++ .../StructuredResponseCreateParams.kt | 528 ++++++++++++++++++ .../responses/StructuredResponseOutputItem.kt | 189 +++++++ .../StructuredResponseOutputMessage.kt | 199 +++++++ .../services/blocking/ResponseService.kt | 13 + .../blocking/chat/ChatCompletionService.kt | 2 +- .../com/openai/core/StructuredOutputsTest.kt | 22 +- .../openai/core/StructuredOutputsTestUtils.kt | 366 ++++++++++++ .../ChatCompletionCreateParamsTest.kt | 2 +- ...tructuredChatCompletionCreateParamsTest.kt | 253 ++------- .../StructuredChatCompletionMessageTest.kt | 66 +-- .../StructuredChatCompletionTest.kt | 184 ++---- .../StructuredResponseCreateParamsTest.kt | 245 ++++++++ .../StructuredResponseOutputItemTest.kt | 204 +++++++ .../StructuredResponseOutputMessageTest.kt | 298 ++++++++++ .../responses/StructuredResponseTest.kt | 245 ++++++++ .../ResponsesStructuredOutputsExample.java | 73 +++ 24 files changed, 2901 insertions(+), 514 deletions(-) create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponse.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseCreateParams.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseOutputItem.kt create mode 100644 openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseOutputMessage.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTestUtils.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseCreateParamsTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseOutputItemTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseOutputMessageTest.kt create mode 100644 openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseTest.kt create mode 100644 openai-java-example/src/main/java/com/openai/example/ResponsesStructuredOutputsExample.java diff --git a/README.md b/README.md index d7366790e..105aa4b98 100644 --- a/README.md +++ b/README.md @@ -411,15 +411,15 @@ class Book { ``` 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 strings can be -derived from the `BoolList.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: +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<>(); +List books = new ArrayList<>(); -StructuredChatCompletionCreateParams params = ChatCompletionCreateParams.builder() - .responseFormat(books.class) +StructuredChatCompletionCreateParams> params = ChatCompletionCreateParams.builder() + .responseFormat(books.getClass()) // ... .build(); ``` @@ -474,6 +474,23 @@ StructuredChatCompletionCreateParams params = ChatCompletionCreatePara 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 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 index 6b1889ff1..747995e12 100644 --- a/openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt +++ b/openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt @@ -12,6 +12,8 @@ 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 @@ -23,11 +25,31 @@ private val MAPPER = .addModule(JavaTimeModule()) .build() +/** + * Builds a response format using a JSON schema derived from the structure of an arbitrary Java + * class. + */ @JvmSynthetic -internal fun fromClass( +internal fun responseFormatFromClass( type: Class, localValidation: JsonSchemaLocalValidation = JsonSchemaLocalValidation.YES, -): ResponseFormatJsonSchema { +): 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) { @@ -39,24 +61,48 @@ internal fun fromClass( } } - return ResponseFormatJsonSchema.builder() - .jsonSchema( - ResponseFormatJsonSchema.JsonSchema.builder() + 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(schema)) + .schema( + ResponseFormatTextJsonSchemaConfig.Schema.builder() + .additionalProperties( + extractAndValidateSchema(type, localValidation) + .fields() + .asSequence() + .associate { it.key to JsonValue.fromJsonNode(it.value) } + ) + .build() + ) // 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 { - // 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. val configBuilder = SchemaGeneratorConfigBuilder( com.github.victools.jsonschema.generator.SchemaVersion.DRAFT_2020_12, @@ -79,10 +125,14 @@ internal fun extractSchema(type: Class): JsonNode { 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 fromJson(json: String, type: Class): T = +internal fun responseTypeFromJson(json: String, responseType: Class): T = try { - MAPPER.readValue(json, type) + 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 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 d012ff94a..85d2582fe 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 @@ -1299,11 +1299,12 @@ private constructor( } /** - * Sets 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. + * 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 responseFormat A class from which a JSON schema will be derived to define the + * @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 @@ -1316,11 +1317,11 @@ private constructor( */ @JvmOverloads fun responseFormat( - responseFormat: Class, + responseType: Class, localValidation: JsonSchemaLocalValidation = JsonSchemaLocalValidation.YES, ) = StructuredChatCompletionCreateParams.builder() - .wrap(responseFormat, this, localValidation) + .wrap(responseType, this, localValidation) /** * This feature is in Beta. If specified, our system will make a best effort to sample 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 index 62cde6f56..872135a4c 100644 --- 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 @@ -18,101 +18,101 @@ import java.util.Optional * @param T The type of the class to which the JSON data in the response will be deserialized. */ class StructuredChatCompletion( - @get:JvmName("responseFormat") val responseFormat: Class, - @get:JvmName("chatCompletion") val chatCompletion: ChatCompletion, + @get:JvmName("responseType") val responseType: Class, + @get:JvmName("rawChatCompletion") val rawChatCompletion: ChatCompletion, ) { /** @see ChatCompletion.id */ - fun id(): String = chatCompletion.id() + fun id(): String = rawChatCompletion.id() private val choices by lazy { - chatCompletion._choices().map { choices -> choices.map { Choice(responseFormat, it) } } + rawChatCompletion._choices().map { choices -> choices.map { Choice(responseType, it) } } } /** @see ChatCompletion.choices */ fun choices(): List> = choices.getRequired("choices") /** @see ChatCompletion.created */ - fun created(): Long = chatCompletion.created() + fun created(): Long = rawChatCompletion.created() /** @see ChatCompletion.model */ - fun model(): String = chatCompletion.model() + fun model(): String = rawChatCompletion.model() /** @see ChatCompletion._object_ */ - fun _object_(): JsonValue = chatCompletion._object_() + fun _object_(): JsonValue = rawChatCompletion._object_() /** @see ChatCompletion.serviceTier */ - fun serviceTier(): Optional = chatCompletion.serviceTier() + fun serviceTier(): Optional = rawChatCompletion.serviceTier() /** @see ChatCompletion.systemFingerprint */ - fun systemFingerprint(): Optional = chatCompletion.systemFingerprint() + fun systemFingerprint(): Optional = rawChatCompletion.systemFingerprint() /** @see ChatCompletion.usage */ - fun usage(): Optional = chatCompletion.usage() + fun usage(): Optional = rawChatCompletion.usage() /** @see ChatCompletion._id */ - fun _id(): JsonField = chatCompletion._id() + fun _id(): JsonField = rawChatCompletion._id() /** @see ChatCompletion._choices */ fun _choices(): JsonField>> = choices /** @see ChatCompletion._created */ - fun _created(): JsonField = chatCompletion._created() + fun _created(): JsonField = rawChatCompletion._created() /** @see ChatCompletion._model */ - fun _model(): JsonField = chatCompletion._model() + fun _model(): JsonField = rawChatCompletion._model() /** @see ChatCompletion._serviceTier */ - fun _serviceTier(): JsonField = chatCompletion._serviceTier() + fun _serviceTier(): JsonField = rawChatCompletion._serviceTier() /** @see ChatCompletion._systemFingerprint */ - fun _systemFingerprint(): JsonField = chatCompletion._systemFingerprint() + fun _systemFingerprint(): JsonField = rawChatCompletion._systemFingerprint() /** @see ChatCompletion._usage */ - fun _usage(): JsonField = chatCompletion._usage() + fun _usage(): JsonField = rawChatCompletion._usage() /** @see ChatCompletion._additionalProperties */ - fun _additionalProperties(): Map = chatCompletion._additionalProperties() + fun _additionalProperties(): Map = rawChatCompletion._additionalProperties() class Choice internal constructor( - @get:JvmName("responseFormat") val responseFormat: Class, - @get:JvmName("choice") val choice: ChatCompletion.Choice, + @get:JvmName("responseType") val responseType: Class, + @get:JvmName("rawChoice") val rawChoice: ChatCompletion.Choice, ) { /** @see ChatCompletion.Choice.finishReason */ - fun finishReason(): FinishReason = choice.finishReason() + fun finishReason(): FinishReason = rawChoice.finishReason() /** @see ChatCompletion.Choice.index */ - fun index(): Long = choice.index() + fun index(): Long = rawChoice.index() /** @see ChatCompletion.Choice.logprobs */ - fun logprobs(): Optional = choice.logprobs() + fun logprobs(): Optional = rawChoice.logprobs() /** @see ChatCompletion.Choice._finishReason */ - fun _finishReason(): JsonField = choice._finishReason() + fun _finishReason(): JsonField = rawChoice._finishReason() private val message by lazy { - choice._message().map { StructuredChatCompletionMessage(responseFormat, it) } + rawChoice._message().map { StructuredChatCompletionMessage(responseType, it) } } /** @see ChatCompletion.Choice.message */ fun message(): StructuredChatCompletionMessage = message.getRequired("message") /** @see ChatCompletion.Choice._index */ - fun _index(): JsonField = choice._index() + fun _index(): JsonField = rawChoice._index() /** @see ChatCompletion.Choice._logprobs */ - fun _logprobs(): JsonField = choice._logprobs() + fun _logprobs(): JsonField = rawChoice._logprobs() /** @see ChatCompletion.Choice._message */ fun _message(): JsonField> = message /** @see ChatCompletion.Choice._additionalProperties */ - fun _additionalProperties(): Map = choice._additionalProperties() + fun _additionalProperties(): Map = rawChoice._additionalProperties() /** @see ChatCompletion.Choice.validate */ fun validate(): Choice = apply { message().validate() - choice.validate() + rawChoice.validate() } /** @see ChatCompletion.Choice.isValid */ @@ -130,22 +130,22 @@ class StructuredChatCompletion( } return other is Choice<*> && - responseFormat == other.responseFormat && - choice == other.choice + responseType == other.responseType && + rawChoice == other.rawChoice } - private val hashCode: Int by lazy { Objects.hash(responseFormat, choice) } + private val hashCode: Int by lazy { Objects.hash(responseType, rawChoice) } override fun hashCode(): Int = hashCode override fun toString() = - "${javaClass.simpleName}{responseFormat=$responseFormat, choice=$choice}" + "${javaClass.simpleName}{responseType=$responseType, rawChoice=$rawChoice}" } /** @see ChatCompletion.validate */ fun validate() = apply { choices().forEach { it.validate() } - chatCompletion.validate() + rawChatCompletion.validate() } /** @see ChatCompletion.isValid */ @@ -163,14 +163,14 @@ class StructuredChatCompletion( } return other is StructuredChatCompletion<*> && - responseFormat == other.responseFormat && - chatCompletion == other.chatCompletion + responseType == other.responseType && + rawChatCompletion == other.rawChatCompletion } - private val hashCode: Int by lazy { Objects.hash(responseFormat, chatCompletion) } + private val hashCode: Int by lazy { Objects.hash(responseType, rawChatCompletion) } override fun hashCode(): Int = hashCode override fun toString() = - "${javaClass.simpleName}{responseFormat=$responseFormat, chatCompletion=$chatCompletion}" + "${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 index 4f6a3a63c..73741b581 100644 --- 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 @@ -4,9 +4,9 @@ import com.openai.core.JsonField import com.openai.core.JsonSchemaLocalValidation import com.openai.core.JsonValue import com.openai.core.checkRequired -import com.openai.core.fromClass 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 @@ -14,16 +14,16 @@ import java.util.Optional /** * A wrapper for [ChatCompletionCreateParams] that provides a type-safe [Builder] that can record - * the type of the [responseFormat] 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_. + * 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("responseFormat") val responseFormat: Class, + @get:JvmName("responseType") val responseType: Class, /** * The raw, underlying chat completion create parameters wrapped by this structured instance of * the parameters. @@ -36,19 +36,19 @@ internal constructor( } class Builder internal constructor() { - private var responseFormat: Class? = null + private var responseType: Class? = null private var paramsBuilder = ChatCompletionCreateParams.builder() @JvmSynthetic internal fun wrap( - responseFormat: Class, + responseType: Class, paramsBuilder: ChatCompletionCreateParams.Builder, localValidation: JsonSchemaLocalValidation, ) = apply { - this.responseFormat = responseFormat + this.responseType = responseType this.paramsBuilder = paramsBuilder // Convert the class to a JSON schema and apply it to the delegate `Builder`. - responseFormat(responseFormat, localValidation) + responseFormat(responseType, localValidation) } /** Injects a given `ChatCompletionCreateParams.Builder`. For use only when testing. */ @@ -407,11 +407,11 @@ internal constructor( */ @JvmOverloads fun responseFormat( - responseFormat: Class, + responseType: Class, localValidation: JsonSchemaLocalValidation = JsonSchemaLocalValidation.YES, ) = apply { - this.responseFormat = responseFormat - paramsBuilder.responseFormat(fromClass(responseFormat, localValidation)) + this.responseType = responseType + paramsBuilder.responseFormat(responseFormatFromClass(responseType, localValidation)) } /** @see ChatCompletionCreateParams.Builder.seed */ @@ -741,7 +741,7 @@ internal constructor( */ fun build() = StructuredChatCompletionCreateParams( - checkRequired("responseFormat", responseFormat), + checkRequired("responseType", responseType), paramsBuilder.build(), ) } @@ -752,12 +752,14 @@ internal constructor( } return other is StructuredChatCompletionCreateParams<*> && - responseFormat == other.responseFormat && + responseType == other.responseType && rawParams == other.rawParams } - override fun hashCode(): Int = Objects.hash(responseFormat, rawParams) + private val hashCode: Int by lazy { Objects.hash(responseType, rawParams) } + + override fun hashCode(): Int = hashCode override fun toString() = - "${javaClass.simpleName}{responseFormat=$responseFormat, params=$rawParams}" + "${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 index b833dd476..27e8a3c74 100644 --- 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 @@ -2,7 +2,7 @@ package com.openai.models.chat.completions import com.openai.core.JsonField import com.openai.core.JsonValue -import com.openai.core.fromJson +import com.openai.core.responseTypeFromJson import com.openai.models.chat.completions.ChatCompletionMessage.FunctionCall import java.util.Objects import java.util.Optional @@ -17,69 +17,64 @@ import java.util.Optional */ class StructuredChatCompletionMessage internal constructor( - @get:JvmName("responseFormat") val responseFormat: Class, - @get:JvmName("chatCompletionMessage") val chatCompletionMessage: ChatCompletionMessage, + @get:JvmName("responseType") val responseType: Class, + @get:JvmName("rawMessage") val rawMessage: ChatCompletionMessage, ) { private val content: JsonField by lazy { - chatCompletionMessage._content().map { fromJson(it, responseFormat) } + rawMessage._content().map { responseTypeFromJson(it, responseType) } } /** @see ChatCompletionMessage.content */ fun content(): Optional = content.getOptional("content") /** @see ChatCompletionMessage.refusal */ - fun refusal(): Optional = chatCompletionMessage.refusal() + fun refusal(): Optional = rawMessage.refusal() /** @see ChatCompletionMessage._role */ - fun _role(): JsonValue = chatCompletionMessage._role() + fun _role(): JsonValue = rawMessage._role() /** @see ChatCompletionMessage.annotations */ - fun annotations(): Optional> = - chatCompletionMessage.annotations() + fun annotations(): Optional> = rawMessage.annotations() /** @see ChatCompletionMessage.audio */ - fun audio(): Optional = chatCompletionMessage.audio() + fun audio(): Optional = rawMessage.audio() /** @see ChatCompletionMessage.functionCall */ - @Deprecated("deprecated") - fun functionCall(): Optional = chatCompletionMessage.functionCall() + @Deprecated("deprecated") fun functionCall(): Optional = rawMessage.functionCall() /** @see ChatCompletionMessage.toolCalls */ - fun toolCalls(): Optional> = - chatCompletionMessage.toolCalls() + fun toolCalls(): Optional> = rawMessage.toolCalls() /** @see ChatCompletionMessage._content */ fun _content(): JsonField = content /** @see ChatCompletionMessage._refusal */ - fun _refusal(): JsonField = chatCompletionMessage._refusal() + fun _refusal(): JsonField = rawMessage._refusal() /** @see ChatCompletionMessage._annotations */ fun _annotations(): JsonField> = - chatCompletionMessage._annotations() + rawMessage._annotations() /** @see ChatCompletionMessage._audio */ - fun _audio(): JsonField = chatCompletionMessage._audio() + fun _audio(): JsonField = rawMessage._audio() /** @see ChatCompletionMessage._functionCall */ @Deprecated("deprecated") - fun _functionCall(): JsonField = chatCompletionMessage._functionCall() + fun _functionCall(): JsonField = rawMessage._functionCall() /** @see ChatCompletionMessage._toolCalls */ - fun _toolCalls(): JsonField> = - chatCompletionMessage._toolCalls() + fun _toolCalls(): JsonField> = rawMessage._toolCalls() /** @see ChatCompletionMessage._additionalProperties */ - fun _additionalProperties(): Map = - 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 = chatCompletionMessage.validate() + fun validate(): ChatCompletionMessage = rawMessage.validate() /** @see ChatCompletionMessage.isValid */ - fun isValid(): Boolean = chatCompletionMessage.isValid() + fun isValid(): Boolean = rawMessage.isValid() override fun equals(other: Any?): Boolean { if (this === other) { @@ -87,14 +82,14 @@ internal constructor( } return other is StructuredChatCompletionMessage<*> && - responseFormat == other.responseFormat && - chatCompletionMessage == other.chatCompletionMessage + responseType == other.responseType && + rawMessage == other.rawMessage } - private val hashCode: Int by lazy { Objects.hash(responseFormat, chatCompletionMessage) } + private val hashCode: Int by lazy { Objects.hash(responseType, rawMessage) } override fun hashCode(): Int = hashCode override fun toString() = - "${javaClass.simpleName}{responseFormat=$responseFormat, chatCompletionMessage=$chatCompletionMessage}" + "${javaClass.simpleName}{responseType=$responseType, rawMessage=$rawMessage}" } 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 6b512ed99..345f6a939 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/StructuredResponse.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponse.kt new file mode 100644 index 000000000..d71450cfd --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponse.kt @@ -0,0 +1,230 @@ +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() + + private var validated: Boolean = false + + /** @see Response.validate */ + fun validate(): StructuredResponse = apply { + if (validated) { + return@apply + } + + id() + createdAt() + error().ifPresent { it.validate() } + incompleteDetails().ifPresent { it.validate() } + instructions() + metadata().ifPresent { it.validate() } + model().validate() + _object_().let { + if (it != JsonValue.from("response")) { + throw OpenAIInvalidDataException("'object_' is invalid, received $it") + } + } + // `output()` is a different type to that in the delegate class. + output().forEach { it.validate() } + parallelToolCalls() + temperature() + toolChoice().validate() + tools().forEach { it.validate() } + topP() + maxOutputTokens() + previousResponseId() + reasoning().ifPresent { it.validate() } + serviceTier().ifPresent { it.validate() } + status().ifPresent { it.validate() } + text().ifPresent { it.validate() } + truncation().ifPresent { it.validate() } + usage().ifPresent { it.validate() } + user() + validated = true + } + + /** @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 000000000..f2adad975 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseCreateParams.kt @@ -0,0 +1,528 @@ +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 + } + + // TODO: Probably not correct, as text config could be overwritten. + /** @see ResponseCreateParams.Builder.body */ + fun body(body: ResponseCreateParams.Body) = apply { paramsBuilder.body(body) } + + /** @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 000000000..39cd6b6b4 --- /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 000000000..e131df2c8 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/StructuredResponseOutputMessage.kt @@ -0,0 +1,199 @@ +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() + + private var validated: Boolean = false + + /** @see ResponseOutputMessage.validate */ + fun validate(): StructuredResponseOutputMessage = apply { + if (validated) { + return@apply + } + + id() + // `content()` is a different type to that in the delegate class. + content().forEach { it.validate() } + _role().let { + if (it != JsonValue.from("assistant")) { + throw OpenAIInvalidDataException("'role' is invalid, received $it") + } + } + status().validate() + _type().let { + if (it != JsonValue.from("message")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + validated = true + } + + /** @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/services/blocking/ResponseService.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/ResponseService.kt index b6e1f9620..695700034 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,17 @@ interface ResponseService { requestOptions: RequestOptions = RequestOptions.none(), ): Response + /** @see create */ + fun create(params: StructuredResponseCreateParams): StructuredResponse = + create(params, RequestOptions.none()) + + /** @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 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 6985e05ea..297921495 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 @@ -65,7 +65,7 @@ interface ChatCompletionService { params: StructuredChatCompletionCreateParams, requestOptions: RequestOptions = RequestOptions.none(), ): StructuredChatCompletion = - StructuredChatCompletion(params.responseFormat, create(params.rawParams, requestOptions)) + StructuredChatCompletion(params.responseType, create(params.rawParams, requestOptions)) /** * **Starting a new project?** We recommend trying 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 index 2c1eb8855..dd5cdd572 100644 --- a/openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/core/StructuredOutputsTest.kt @@ -447,7 +447,7 @@ internal class StructuredOutputsTest { @Test fun schemaTest_tinyRecursiveSchema() { - @Suppress("unused") class X(val s: String, val x: X) + @Suppress("unused") class X(val s: String, val x: X?) schema = extractSchema(X::class.java) validator.validate(schema) @@ -633,8 +633,7 @@ internal class StructuredOutputsTest { ) validator.validate(schema) - // TODO: Decide if this is the expected behavior, i.e., that it is OK for an "object" schema - // to have no "properties". + // For now, allow that an object may have no properties. Update this if that is revised. assertThat(validator.isValid()).isTrue() } @@ -1415,7 +1414,7 @@ internal class StructuredOutputsTest { fun fromJsonSuccess() { @Suppress("unused") class X(val s: String) - val x = fromJson("{\"s\" : \"hello\"}", X::class.java) + val x = responseTypeFromJson("{\"s\" : \"hello\"}", X::class.java) assertThat(x.s).isEqualTo("hello") } @@ -1425,7 +1424,7 @@ internal class StructuredOutputsTest { @Suppress("unused") class X(val s: String) // Well-formed JSON, but it does not match the schema of class `X`. - assertThatThrownBy { fromJson("{\"wrong\" : \"hello\"}", X::class.java) } + assertThatThrownBy { responseTypeFromJson("{\"wrong\" : \"hello\"}", X::class.java) } .isExactlyInstanceOf(OpenAIInvalidDataException::class.java) .hasMessage("Error parsing JSON: {\"wrong\" : \"hello\"}") } @@ -1435,7 +1434,7 @@ internal class StructuredOutputsTest { @Suppress("unused") class X(val s: String) // Malformed JSON. - assertThatThrownBy { fromJson("{\"truncated", X::class.java) } + assertThatThrownBy { responseTypeFromJson("{\"truncated", X::class.java) } .isExactlyInstanceOf(OpenAIInvalidDataException::class.java) .hasMessage("Error parsing JSON: {\"truncated") } @@ -1444,7 +1443,7 @@ internal class StructuredOutputsTest { fun fromClassEnablesStrictAdherenceToSchema() { @Suppress("unused") class X(val s: String) - val jsonSchema = fromClass(X::class.java) + 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. @@ -1464,7 +1463,7 @@ internal class StructuredOutputsTest { class Z(val y: Y) assertThatNoException().isThrownBy { - fromClass(Z::class.java, JsonSchemaLocalValidation.NO) + responseFormatFromClass(Z::class.java, JsonSchemaLocalValidation.NO) } } @@ -1473,7 +1472,7 @@ internal class StructuredOutputsTest { @Suppress("unused") class X(val s: String) assertThatNoException().isThrownBy { - fromClass(X::class.java, JsonSchemaLocalValidation.YES) + responseFormatFromClass(X::class.java, JsonSchemaLocalValidation.YES) } } @@ -1488,7 +1487,7 @@ internal class StructuredOutputsTest { class Y(val x: X) class Z(val y: Y) - assertThatThrownBy { fromClass(Z::class.java, JsonSchemaLocalValidation.YES) } + assertThatThrownBy { responseFormatFromClass(Z::class.java, JsonSchemaLocalValidation.YES) } .isExactlyInstanceOf(IllegalArgumentException::class.java) .hasMessage( "Local validation failed for JSON schema derived from ${Z::class.java}:\n" + @@ -1509,7 +1508,8 @@ internal class StructuredOutputsTest { class Y(val x: X) class Z(val y: Y) - assertThatThrownBy { fromClass(Z::class.java) } // Use default for `localValidation` flag. + // 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" + 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 000000000..c62eb8ed9 --- /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/chat/completions/ChatCompletionCreateParamsTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParamsTest.kt index fb52ffc6e..6ae11ba15 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 @@ -367,7 +367,7 @@ internal class ChatCompletionCreateParamsTest { val body = params.rawParams._body() assertThat(params).isInstanceOf(StructuredChatCompletionCreateParams::class.java) - assertThat(params.responseFormat).isEqualTo(X::class.java) + assertThat(params.responseType).isEqualTo(X::class.java) assertThat(body.messages()) .containsExactly( ChatCompletionMessageParam.ofDeveloper( 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 index 4abd66b67..4b2891982 100644 --- 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 @@ -1,23 +1,31 @@ package com.openai.models.chat.completions -import com.openai.core.fromClass +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 com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.JSON_FIELD -import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.JSON_VALUE -import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.MESSAGE -import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.OPTIONAL -import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.STRING -import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.X -import java.lang.reflect.Method -import kotlin.collections.plus -import kotlin.reflect.full.declaredFunctions -import kotlin.reflect.jvm.javaMethod -import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test -import org.junit.jupiter.api.fail import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mockito.mock @@ -39,140 +47,11 @@ import org.mockito.kotlin.verify */ internal class StructuredChatCompletionCreateParamsTest { companion object { - private 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`. - */ - private 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." } - } - - // Using `if` and `fail`, so the compiler knows the code will not continue and can infer - // that `delegationMethod` is not null. It cannot do this for `assertThat...isNotNull`. - if (method == null) { - fail { "Function $testCase cannot be found in $target." } - } - - return method - } - - 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 - } - - private val NULLABLE = null - private const val BOOLEAN: Boolean = true - private val NULLABLE_BOOLEAN: Boolean? = null - private const val LONG: Long = 42L - private val NULLABLE_LONG: Long? = null - private const val DOUBLE: Double = 42.0 - private val NULLABLE_DOUBLE: Double? = null - private val LIST = listOf(STRING) - private val SET = setOf(STRING) - private val MAP = mapOf(STRING to STRING) - 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 = @@ -226,21 +105,10 @@ internal class StructuredChatCompletionCreateParamsTest { private val HEADERS = Headers.builder().build() private val QUERY_PARAMS = QueryParams.builder().build() - // Want `vararg`, so cannot use `data class`. Need a custom `toString`, anyway. - class DelegationWriteTestCase(val functionName: String, 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" - }})" - } - // The list order follows the declaration order in `ChatCompletionCreateParams.Builder` for // easier maintenance. @JvmStatic - fun builderDelegationTestCases() = + private fun builderDelegationTestCases() = listOf( DelegationWriteTestCase("body", PARAMS_BODY), DelegationWriteTestCase("messages", LIST), @@ -394,82 +262,33 @@ internal class StructuredChatCompletionCreateParamsTest { // 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). - val mockBuilderDelegate: ChatCompletionCreateParams.Builder = + private val mockBuilderDelegate: ChatCompletionCreateParams.Builder = mock(ChatCompletionCreateParams.Builder::class.java) - val builderDelegator = + private val builderDelegator = StructuredChatCompletionCreateParams.builder().inject(mockBuilderDelegate) @Test fun allBuilderDelegateFunctionsExistInDelegator() { // The delegator class does not implement the various `responseFormat` functions of the // delegate class. - StructuredChatCompletionTest.checkAllDelegation( - ChatCompletionCreateParams.Builder::class, - StructuredChatCompletionCreateParams.Builder::class, - "responseFormat", - ) + checkAllDelegation(mockBuilderDelegate::class, builderDelegator::class, "responseFormat") } @Test fun allBuilderDelegatorFunctionsExistInDelegate() { // The delegator implements a different `responseFormat` function from those overloads in // the delegate class. - StructuredChatCompletionTest.checkAllDelegation( - StructuredChatCompletionCreateParams.Builder::class, - ChatCompletionCreateParams.Builder::class, - "responseFormat", - ) + checkAllDelegation(builderDelegator::class, mockBuilderDelegate::class, "responseFormat") } @Test fun allBuilderDelegatorFunctionsAreTested() { - // 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 are (at this - // time) 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 exceptionalTestedFns = listOf("responseFormat") - val testedFns = - (builderDelegationTestCases().map { it.functionName } + exceptionalTestedFns) - .toMutableList() - val nonDelegatingFns = listOf("build", "wrap", "inject") - - val delegatorFns = - StructuredChatCompletionCreateParams.Builder::class.declaredFunctions.toMutableList() - - // Making concurrent modifications to the list, so using an `Iterator`. - val i = delegatorFns.iterator() - - while (i.hasNext()) { - val functionName = i.next().name - - if (functionName in testedFns) { - testedFns.remove(functionName) - i.remove() - } - if (functionName in nonDelegatingFns) { - i.remove() - } - } - - // If there are function names remaining in `delegatorFns`, then there are tests missing. - // Only report the names of the functions not tested: parameters are not matched, so any - // signatures could be misleading. - assertThat(delegatorFns) - .describedAs { - "Delegation is not tested for functions ${delegatorFns.map { it.name }}." - } - .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() + checkAllDelegatorWriteFunctionsAreTested( + builderDelegator::class, + builderDelegationTestCases(), + exceptionalTestedFns = setOf("responseFormat"), + nonDelegatingFns = setOf("build", "wrap", "inject"), + ) } @ParameterizedTest @@ -485,7 +304,7 @@ internal class StructuredChatCompletionCreateParamsTest { val delegatorTestCase = DelegationWriteTestCase("responseFormat", X::class.java) val delegatorMethod = findDelegationMethod(builderDelegator, delegatorTestCase) val mockDelegateTestCase = - DelegationWriteTestCase("responseFormat", fromClass(X::class.java)) + DelegationWriteTestCase("responseFormat", responseFormatFromClass(X::class.java)) val mockDelegateMethod = findDelegationMethod(mockBuilderDelegate, mockDelegateTestCase) delegatorMethod.invoke(builderDelegator, delegatorTestCase.inputValues[0]) 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 index 347788a35..939f9a538 100644 --- 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 @@ -1,15 +1,16 @@ 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.models.chat.completions.StructuredChatCompletionTest.Companion.DelegationReadTestCase -import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.JSON_FIELD -import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.JSON_VALUE -import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.MESSAGE -import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.OPTIONAL -import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.X -import com.openai.models.chat.completions.StructuredChatCompletionTest.Companion.checkOneDelegationRead +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 kotlin.reflect.full.declaredFunctions import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest @@ -33,10 +34,13 @@ import org.mockito.kotlin.verify */ internal class StructuredChatCompletionMessageTest { companion object { - // The list order follows the declaration order in `StructuredChatCompletionMessage` for - // easier maintenance. See `StructuredChatCompletionTest` for details on the values used. + private val MESSAGE = + ChatCompletionMessage.builder().content(STRING).refusal(STRING).build() + + // The list order follows the declaration order in `ChatCompletionMessage` for easier + // maintenance. @JvmStatic - fun delegationTestCases() = + private fun delegationTestCases() = listOf( // `content()` is a special case and has its own test function. DelegationReadTestCase("refusal", OPTIONAL), @@ -62,46 +66,30 @@ internal class StructuredChatCompletionMessageTest { // 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). - val mockDelegate: ChatCompletionMessage = mock(ChatCompletionMessage::class.java) - val delegator = StructuredChatCompletionMessage(X::class.java, mockDelegate) + private val mockDelegate: ChatCompletionMessage = mock(ChatCompletionMessage::class.java) + private val delegator = StructuredChatCompletionMessage(X::class.java, mockDelegate) @Test fun allDelegateFunctionsExistInDelegator() { - StructuredChatCompletionTest.checkAllDelegation( - ChatCompletionMessage::class, - StructuredChatCompletionMessage::class, - "toBuilder", - "toParam", - ) + checkAllDelegation(mockDelegate::class, delegator::class, "toBuilder", "toParam") } @Test fun allDelegatorFunctionsExistInDelegate() { - StructuredChatCompletionTest.checkAllDelegation( - StructuredChatCompletionMessage::class, - ChatCompletionMessage::class, - ) + 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. - val exceptionalTestedFns = setOf("content", "_content") - val testedFns = delegationTestCases().map { it.functionName }.toSet() + exceptionalTestedFns - // A few delegator functions do not delegate, so no test function is necessary. - val nonDelegatingFns = listOf("equals", "hashCode", "toString") - - val delegatorFunctions = StructuredChatCompletionMessage::class.declaredFunctions - - for (delegatorFunction in delegatorFunctions) { - assertThat( - delegatorFunction.name in testedFns || - delegatorFunction.name in nonDelegatingFns - ) - .describedAs("Delegation is not tested for function '${delegatorFunction.name}.") - .isTrue - } + // 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 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 index af380bbf6..40dc509f3 100644 --- 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 @@ -1,12 +1,17 @@ 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.JsonValue +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 java.util.Optional -import kotlin.reflect.KClass -import kotlin.reflect.KVisibility -import kotlin.reflect.full.declaredFunctions import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest @@ -30,75 +35,7 @@ import org.mockito.kotlin.verify */ internal class StructuredChatCompletionTest { companion object { - internal fun checkAllDelegation( - delegateClass: KClass<*>, - delegatorClass: KClass<*>, - vararg exceptFunctionNames: String, - ) { - assertThat(delegateClass != delegatorClass) - .describedAs { "Delegate and delegator classes should not be the same." } - .isTrue - - val delegateFunctions = delegateClass.declaredFunctions - - for (delegateFunction in delegateFunctions) { - if (delegateFunction.visibility != KVisibility.PUBLIC) { - // Non-public methods are just implementation details of each class. - continue - } - - if (delegateFunction.name in exceptFunctionNames) { - // Ignore functions that are known exceptions (e.g., `toBuilder`). - 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 delegatorFunction = - delegatorClass.declaredFunctions.find { - it.name == delegateFunction.name && - it.parameters.drop(1).map { it.type } == - delegateFunction.parameters.drop(1).map { it.type } - } - - assertThat(delegatorFunction != null) - .describedAs { - "Function $delegateFunction is not found in ${delegatorClass.simpleName}." - } - .isTrue - } - } - - 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) - } - - // 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 with the test. Constants have been provided for this purpose. - internal const val STRING = "a-string" - - internal val OPTIONAL = Optional.of(STRING) - internal val JSON_FIELD = JsonField.of(STRING) - internal val JSON_VALUE = JsonValue.from(STRING) - internal val MESSAGE = + private val MESSAGE = ChatCompletionMessage.builder().content(STRING).refusal(STRING).build() private val FINISH_REASON = ChatCompletion.Choice.FinishReason.STOP private val CHOICE = @@ -111,16 +48,13 @@ internal class StructuredChatCompletionTest { ) .build() - data class DelegationReadTestCase(val functionName: String, val expectedValue: Any) - - // The list order follows the declaration order in `StructuredChatCompletionMessage` for - // easier maintenance. + // The list order follows the declaration order in `ChatCompletion` for easier maintenance. @JvmStatic - fun delegationTestCases() = + private fun delegationTestCases() = listOf( DelegationReadTestCase("id", STRING), // `choices()` is a special case and has its own test function. - DelegationReadTestCase("created", 123L), + DelegationReadTestCase("created", LONG), DelegationReadTestCase("model", STRING), DelegationReadTestCase("_object_", JSON_VALUE), DelegationReadTestCase("serviceTier", OPTIONAL), @@ -139,10 +73,10 @@ internal class StructuredChatCompletionTest { ) @JvmStatic - fun choiceDelegationTestCases() = + private fun choiceDelegationTestCases() = listOf( DelegationReadTestCase("finishReason", FINISH_REASON), - DelegationReadTestCase("index", 123L), + DelegationReadTestCase("index", LONG), DelegationReadTestCase("logprobs", OPTIONAL), DelegationReadTestCase("_finishReason", JSON_FIELD), // `message()` is a special case and has its own test function. @@ -153,90 +87,58 @@ internal class StructuredChatCompletionTest { // `validate()` and `isValid()` (which calls `validate()`) are tested separately, // as they require special handling. ) - - /** 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() - } } // 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). - val mockDelegate: ChatCompletion = mock(ChatCompletion::class.java) - val delegator = StructuredChatCompletion(X::class.java, mockDelegate) + private val mockDelegate: ChatCompletion = mock(ChatCompletion::class.java) + private val delegator = StructuredChatCompletion(X::class.java, mockDelegate) - val mockChoiceDelegate: ChatCompletion.Choice = mock(ChatCompletion.Choice::class.java) - val choiceDelegator = StructuredChatCompletion.Choice(X::class.java, mockChoiceDelegate) + private val mockChoiceDelegate: ChatCompletion.Choice = mock(ChatCompletion.Choice::class.java) + private val choiceDelegator = + StructuredChatCompletion.Choice(X::class.java, mockChoiceDelegate) @Test fun allChatCompletionDelegateFunctionsExistInDelegator() { - checkAllDelegation(ChatCompletion::class, StructuredChatCompletion::class, "toBuilder") + checkAllDelegation(mockDelegate::class, delegator::class, "toBuilder") } @Test fun allChatCompletionDelegatorFunctionsExistInDelegate() { - checkAllDelegation(StructuredChatCompletion::class, ChatCompletion::class) + checkAllDelegation(delegator::class, mockDelegate::class) } @Test fun allChoiceDelegateFunctionsExistInDelegator() { - checkAllDelegation( - ChatCompletion.Choice::class, - StructuredChatCompletion.Choice::class, - "toBuilder", - ) + checkAllDelegation(mockChoiceDelegate::class, choiceDelegator::class, "toBuilder") } @Test fun allChoiceDelegatorFunctionsExistInDelegate() { - checkAllDelegation(StructuredChatCompletion.Choice::class, ChatCompletion.Choice::class) + 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. - val exceptionalTestedFns = setOf("choices", "_choices", "validate", "isValid") - val testedFns = delegationTestCases().map { it.functionName }.toSet() + exceptionalTestedFns - // A few delegator functions do not delegate, so no test function is necessary. - val nonDelegatingFns = listOf("equals", "hashCode", "toString") - - val delegatorFunctions = StructuredChatCompletion::class.declaredFunctions - - for (delegatorFunction in delegatorFunctions) { - assertThat( - delegatorFunction.name in testedFns || - delegatorFunction.name in nonDelegatingFns - ) - .describedAs("Delegation is not tested for function '${delegatorFunction.name}.") - .isTrue - } + // 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() { - // There are exceptional test cases for some functions. Most other functions are part of the - // list of those using the parameterized test. - val exceptionalTestedFns = setOf("message", "_message", "validate", "isValid") - val testedFns = - choiceDelegationTestCases().map { it.functionName }.toSet() + exceptionalTestedFns - // A few delegator functions do not delegate, so no test function is necessary. - val nonDelegatingFns = listOf("equals", "hashCode", "toString") - - val delegatorFunctions = StructuredChatCompletion.Choice::class.declaredFunctions - - for (delegatorFunction in delegatorFunctions) { - assertThat( - delegatorFunction.name in testedFns || - delegatorFunction.name in nonDelegatingFns - ) - .describedAs( - "Delegation is not tested for function 'Choice.${delegatorFunction.name}." - ) - .isTrue - } + checkAllDelegatorReadFunctionsAreTested( + choiceDelegator::class, + choiceDelegationTestCases(), + exceptionalTestedFns = setOf("message", "_message", "validate", "isValid"), + nonDelegatingFns = setOf("equals", "hashCode", "toString"), + ) } @ParameterizedTest @@ -263,7 +165,7 @@ internal class StructuredChatCompletionTest { verify(mockDelegate, times(1))._choices() verifyNoMoreInteractions(mockDelegate) - assertThat(output[0].choice).isEqualTo(CHOICE) + assertThat(output[0].rawChoice).isEqualTo(CHOICE) } @Test @@ -277,7 +179,7 @@ internal class StructuredChatCompletionTest { verify(mockDelegate, times(1))._choices() verifyNoMoreInteractions(mockDelegate) - assertThat(output.getRequired("_choices")[0].choice).isEqualTo(CHOICE) + assertThat(output.getRequired("_choices")[0].rawChoice).isEqualTo(CHOICE) } @Test @@ -339,7 +241,7 @@ internal class StructuredChatCompletionTest { verify(mockChoiceDelegate, times(1))._message() verifyNoMoreInteractions(mockChoiceDelegate) - assertThat(output.chatCompletionMessage).isEqualTo(MESSAGE) + assertThat(output.rawMessage).isEqualTo(MESSAGE) } @Test @@ -353,7 +255,7 @@ internal class StructuredChatCompletionTest { verify(mockChoiceDelegate, times(1))._message() verifyNoMoreInteractions(mockChoiceDelegate) - assertThat(output.getRequired("_message").chatCompletionMessage).isEqualTo(MESSAGE) + assertThat(output.getRequired("_message").rawMessage).isEqualTo(MESSAGE) } @Test 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 000000000..1eb68ee92 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseCreateParamsTest.kt @@ -0,0 +1,245 @@ +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 PARAMS_BODY = + ResponseCreateParams.Body.builder().input(PARAMS_INPUT).model(RESPONSES_MODEL).build() + + 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( + DelegationWriteTestCase("body", PARAMS_BODY), + 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 of the delegate + // class. + checkAllDelegation(mockBuilderDelegate::class, builderDelegator::class, "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 000000000..2ba6fa031 --- /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 000000000..ee3c3a57e --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseOutputMessageTest.kt @@ -0,0 +1,298 @@ +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.JsonValue +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))) + `when`(mockDelegate._role()).thenReturn(JsonValue.from("assistant")) + `when`(mockDelegate.status()).thenReturn(ResponseOutputMessage.Status.COMPLETED) + `when`(mockDelegate._type()).thenReturn(JsonValue.from("message")) + + delegator.validate() + + // Delegator's `validate()` does not call delegate's `validate()`. `_content` is called + // indirectly via the `content` field initializer. + verify(mockDelegate, times(1))._content() + verify(mockDelegate, times(1)).id() + verify(mockDelegate, times(1))._role() + verify(mockDelegate, times(1)).status() + verify(mockDelegate, times(1))._type() + 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))) + `when`(mockDelegate._role()).thenReturn(JsonValue.from("assistant")) + `when`(mockDelegate.status()).thenReturn(ResponseOutputMessage.Status.COMPLETED) + `when`(mockDelegate._type()).thenReturn(JsonValue.from("message")) + + delegator.isValid() + + verify(mockDelegate, times(1))._content() + verify(mockDelegate, times(1)).id() + verify(mockDelegate, times(1))._role() + verify(mockDelegate, times(1)).status() + verify(mockDelegate, times(1))._type() + 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 000000000..365476e79 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/StructuredResponseTest.kt @@ -0,0 +1,245 @@ +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.JsonValue +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.model()).thenReturn(RESPONSES_MODEL) + `when`(mockDelegate._object_()).thenReturn(JsonValue.from("response")) + `when`(mockDelegate._output()).thenReturn(JsonField.of(listOf(OUTPUT_ITEM))) + `when`(mockDelegate.toolChoice()).thenReturn(TOOL_CHOICE) + + delegator.validate() + + // Delegator's `validate()` does not call delegate's `validate()`. `_content` is called + // indirectly via the `content` field initializer. + verify(mockDelegate, times(1)).id() + verify(mockDelegate, times(1)).createdAt() + verify(mockDelegate, times(1)).error() + verify(mockDelegate, times(1)).incompleteDetails() + verify(mockDelegate, times(1)).instructions() + verify(mockDelegate, times(1)).metadata() + verify(mockDelegate, times(1)).model() + verify(mockDelegate, times(1))._object_() + verify(mockDelegate, times(1))._output() // Indirect + verify(mockDelegate, times(1)).parallelToolCalls() + verify(mockDelegate, times(1)).temperature() + verify(mockDelegate, times(1)).toolChoice() + verify(mockDelegate, times(1)).tools() + verify(mockDelegate, times(1)).topP() + verify(mockDelegate, times(1)).maxOutputTokens() + verify(mockDelegate, times(1)).previousResponseId() + verify(mockDelegate, times(1)).reasoning() + verify(mockDelegate, times(1)).serviceTier() + verify(mockDelegate, times(1)).status() + verify(mockDelegate, times(1)).text() + verify(mockDelegate, times(1)).truncation() + verify(mockDelegate, times(1)).usage() + verify(mockDelegate, times(1)).user() + verifyNoMoreInteractions(mockDelegate) + } + + @Test + fun `delegation of isValid`() { + `when`(mockDelegate.model()).thenReturn(RESPONSES_MODEL) + `when`(mockDelegate._object_()).thenReturn(JsonValue.from("response")) + `when`(mockDelegate._output()).thenReturn(JsonField.of(listOf(OUTPUT_ITEM))) + `when`(mockDelegate.toolChoice()).thenReturn(TOOL_CHOICE) + + // `isValid()` calls `validate()`, so the test is more-or-less the same. + delegator.isValid() + + verify(mockDelegate, times(1)).id() + verify(mockDelegate, times(1)).createdAt() + verify(mockDelegate, times(1)).error() + verify(mockDelegate, times(1)).incompleteDetails() + verify(mockDelegate, times(1)).instructions() + verify(mockDelegate, times(1)).metadata() + verify(mockDelegate, times(1)).model() + verify(mockDelegate, times(1))._object_() + verify(mockDelegate, times(1))._output() // Indirect + verify(mockDelegate, times(1)).parallelToolCalls() + verify(mockDelegate, times(1)).temperature() + verify(mockDelegate, times(1)).toolChoice() + verify(mockDelegate, times(1)).tools() + verify(mockDelegate, times(1)).topP() + verify(mockDelegate, times(1)).maxOutputTokens() + verify(mockDelegate, times(1)).previousResponseId() + verify(mockDelegate, times(1)).reasoning() + verify(mockDelegate, times(1)).serviceTier() + verify(mockDelegate, times(1)).status() + verify(mockDelegate, times(1)).text() + verify(mockDelegate, times(1)).truncation() + verify(mockDelegate, times(1)).usage() + verify(mockDelegate, times(1)).user() + verifyNoMoreInteractions(mockDelegate) + } +} 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 000000000..9ef891831 --- /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)); + } +} From c2a9508650420d628b2ff27c303b8963edb611cb Mon Sep 17 00:00:00 2001 From: D Gardner Date: Wed, 14 May 2025 15:51:19 +0100 Subject: [PATCH 15/17] structured-outputs: removed support for Responses params Builder.body function --- .../models/responses/StructuredResponseCreateParams.kt | 4 +--- .../responses/StructuredResponseCreateParamsTest.kt | 10 ++++------ 2 files changed, 5 insertions(+), 9 deletions(-) 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 index f2adad975..aae191ac7 100644 --- 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 @@ -57,9 +57,7 @@ class StructuredResponseCreateParams( this.paramsBuilder = paramsBuilder } - // TODO: Probably not correct, as text config could be overwritten. - /** @see ResponseCreateParams.Builder.body */ - fun body(body: ResponseCreateParams.Body) = apply { paramsBuilder.body(body) } + // The `body(...)` function is deliberately not supported. /** @see ResponseCreateParams.Builder.input */ fun input(input: ResponseCreateParams.Input) = apply { paramsBuilder.input(input) } 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 index 1eb68ee92..3d2050729 100644 --- 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 @@ -52,8 +52,6 @@ internal class StructuredResponseCreateParamsTest { 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 PARAMS_BODY = - ResponseCreateParams.Body.builder().input(PARAMS_INPUT).model(RESPONSES_MODEL).build() private val INCLUDABLE = ResponseIncludable.of(STRING) private val METADATA = ResponseCreateParams.Metadata.builder().build() @@ -87,7 +85,7 @@ internal class StructuredResponseCreateParamsTest { @JvmStatic private fun builderDelegationTestCases() = listOf( - DelegationWriteTestCase("body", PARAMS_BODY), + // The `body(...)` function is deliberately not supported: too messy. DelegationWriteTestCase("input", PARAMS_INPUT), DelegationWriteTestCase("input", JSON_FIELD), DelegationWriteTestCase("input", STRING), @@ -196,9 +194,9 @@ internal class StructuredResponseCreateParamsTest { @Test fun allBuilderDelegateFunctionsExistInDelegator() { - // The delegator class does not implement the various `text` functions of the delegate - // class. - checkAllDelegation(mockBuilderDelegate::class, builderDelegator::class, "text") + // 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 From 3edd9504e5be40422e7dc489923512ee5c3dd493 Mon Sep 17 00:00:00 2001 From: D Gardner Date: Wed, 14 May 2025 17:38:49 +0100 Subject: [PATCH 16/17] structured-outputs: extra docs and simpler code --- .../com/openai/core/StructuredOutputs.kt | 11 +--- .../models/responses/StructuredResponse.kt | 35 +---------- .../StructuredResponseOutputMessage.kt | 20 +------ .../services/blocking/ResponseService.kt | 14 ++++- .../blocking/chat/ChatCompletionService.kt | 16 ++++- .../StructuredResponseOutputMessageTest.kt | 21 ++----- .../responses/StructuredResponseTest.kt | 60 ++----------------- 7 files changed, 39 insertions(+), 138 deletions(-) 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 index 747995e12..df48c1af2 100644 --- a/openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt +++ b/openai-java-core/src/main/kotlin/com/openai/core/StructuredOutputs.kt @@ -77,16 +77,7 @@ internal fun textConfigFromClass( .format( ResponseFormatTextJsonSchemaConfig.builder() .name("json-schema-from-${type.simpleName}") - .schema( - ResponseFormatTextJsonSchemaConfig.Schema.builder() - .additionalProperties( - extractAndValidateSchema(type, localValidation) - .fields() - .asSequence() - .associate { it.key to JsonValue.fromJsonNode(it.value) } - ) - .build() - ) + .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) 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 index d71450cfd..f28865b0c 100644 --- 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 @@ -164,43 +164,10 @@ class StructuredResponse( /** @see Response._additionalProperties */ fun _additionalProperties(): Map = rawResponse._additionalProperties() - private var validated: Boolean = false - /** @see Response.validate */ fun validate(): StructuredResponse = apply { - if (validated) { - return@apply - } - - id() - createdAt() - error().ifPresent { it.validate() } - incompleteDetails().ifPresent { it.validate() } - instructions() - metadata().ifPresent { it.validate() } - model().validate() - _object_().let { - if (it != JsonValue.from("response")) { - throw OpenAIInvalidDataException("'object_' is invalid, received $it") - } - } - // `output()` is a different type to that in the delegate class. output().forEach { it.validate() } - parallelToolCalls() - temperature() - toolChoice().validate() - tools().forEach { it.validate() } - topP() - maxOutputTokens() - previousResponseId() - reasoning().ifPresent { it.validate() } - serviceTier().ifPresent { it.validate() } - status().ifPresent { it.validate() } - text().ifPresent { it.validate() } - truncation().ifPresent { it.validate() } - usage().ifPresent { it.validate() } - user() - validated = true + rawResponse.validate() } /** @see Response.isValid */ 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 index e131df2c8..b7083a251 100644 --- 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 @@ -52,29 +52,11 @@ class StructuredResponseOutputMessage( /** @see ResponseOutputMessage._additionalProperties */ fun _additionalProperties(): Map = rawMessage._additionalProperties() - private var validated: Boolean = false - /** @see ResponseOutputMessage.validate */ fun validate(): StructuredResponseOutputMessage = apply { - if (validated) { - return@apply - } - - id() // `content()` is a different type to that in the delegate class. content().forEach { it.validate() } - _role().let { - if (it != JsonValue.from("assistant")) { - throw OpenAIInvalidDataException("'role' is invalid, received $it") - } - } - status().validate() - _type().let { - if (it != JsonValue.from("message")) { - throw OpenAIInvalidDataException("'type' is invalid, received $it") - } - } - validated = true + rawMessage.validate() } /** @see ResponseOutputMessage.isValid */ 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 695700034..12ed80ddd 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 @@ -44,11 +44,21 @@ interface ResponseService { requestOptions: RequestOptions = RequestOptions.none(), ): Response - /** @see create */ + /** + * 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()) - /** @see create */ + /** + * 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(), 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 297921495..43f372d2a 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 @@ -55,12 +55,24 @@ interface ChatCompletionService { requestOptions: RequestOptions = RequestOptions.none(), ): ChatCompletion - /** @see create */ + /** + * 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()) - /** @see create */ + /** + * 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(), 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 index ee3c3a57e..2b093cf56 100644 --- 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 @@ -4,7 +4,6 @@ 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.JsonValue import com.openai.core.MAP import com.openai.core.OPTIONAL import com.openai.core.STRING @@ -188,19 +187,13 @@ internal class StructuredResponseOutputMessageTest { @Test fun `delegation of validate`() { `when`(mockDelegate._content()).thenReturn(JsonField.of(listOf(CONTENT))) - `when`(mockDelegate._role()).thenReturn(JsonValue.from("assistant")) - `when`(mockDelegate.status()).thenReturn(ResponseOutputMessage.Status.COMPLETED) - `when`(mockDelegate._type()).thenReturn(JsonValue.from("message")) delegator.validate() - // Delegator's `validate()` does not call delegate's `validate()`. `_content` is called - // indirectly via the `content` field initializer. + // 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)).id() - verify(mockDelegate, times(1))._role() - verify(mockDelegate, times(1)).status() - verify(mockDelegate, times(1))._type() + verify(mockDelegate, times(1)).validate() verifyNoMoreInteractions(mockDelegate) } @@ -208,17 +201,11 @@ internal class StructuredResponseOutputMessageTest { fun `delegation of isValid`() { // `isValid` calls `validate()`, so the test is similar to that for `validate()`. `when`(mockDelegate._content()).thenReturn(JsonField.of(listOf(CONTENT))) - `when`(mockDelegate._role()).thenReturn(JsonValue.from("assistant")) - `when`(mockDelegate.status()).thenReturn(ResponseOutputMessage.Status.COMPLETED) - `when`(mockDelegate._type()).thenReturn(JsonValue.from("message")) delegator.isValid() verify(mockDelegate, times(1))._content() - verify(mockDelegate, times(1)).id() - verify(mockDelegate, times(1))._role() - verify(mockDelegate, times(1)).status() - verify(mockDelegate, times(1))._type() + verify(mockDelegate, times(1)).validate() verifyNoMoreInteractions(mockDelegate) } 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 index 365476e79..9a1b76cd1 100644 --- 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 @@ -6,7 +6,6 @@ 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.JsonValue import com.openai.core.LIST import com.openai.core.MAP import com.openai.core.OPTIONAL @@ -172,74 +171,27 @@ internal class StructuredResponseTest { @Test fun `delegation of validate`() { - `when`(mockDelegate.model()).thenReturn(RESPONSES_MODEL) - `when`(mockDelegate._object_()).thenReturn(JsonValue.from("response")) `when`(mockDelegate._output()).thenReturn(JsonField.of(listOf(OUTPUT_ITEM))) - `when`(mockDelegate.toolChoice()).thenReturn(TOOL_CHOICE) delegator.validate() - // Delegator's `validate()` does not call delegate's `validate()`. `_content` is called - // indirectly via the `content` field initializer. - verify(mockDelegate, times(1)).id() - verify(mockDelegate, times(1)).createdAt() - verify(mockDelegate, times(1)).error() - verify(mockDelegate, times(1)).incompleteDetails() - verify(mockDelegate, times(1)).instructions() - verify(mockDelegate, times(1)).metadata() - verify(mockDelegate, times(1)).model() - verify(mockDelegate, times(1))._object_() + // 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)).parallelToolCalls() - verify(mockDelegate, times(1)).temperature() - verify(mockDelegate, times(1)).toolChoice() - verify(mockDelegate, times(1)).tools() - verify(mockDelegate, times(1)).topP() - verify(mockDelegate, times(1)).maxOutputTokens() - verify(mockDelegate, times(1)).previousResponseId() - verify(mockDelegate, times(1)).reasoning() - verify(mockDelegate, times(1)).serviceTier() - verify(mockDelegate, times(1)).status() - verify(mockDelegate, times(1)).text() - verify(mockDelegate, times(1)).truncation() - verify(mockDelegate, times(1)).usage() - verify(mockDelegate, times(1)).user() + verify(mockDelegate, times(1)).validate() verifyNoMoreInteractions(mockDelegate) } @Test fun `delegation of isValid`() { - `when`(mockDelegate.model()).thenReturn(RESPONSES_MODEL) - `when`(mockDelegate._object_()).thenReturn(JsonValue.from("response")) + // `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))) - `when`(mockDelegate.toolChoice()).thenReturn(TOOL_CHOICE) - // `isValid()` calls `validate()`, so the test is more-or-less the same. delegator.isValid() - verify(mockDelegate, times(1)).id() - verify(mockDelegate, times(1)).createdAt() - verify(mockDelegate, times(1)).error() - verify(mockDelegate, times(1)).incompleteDetails() - verify(mockDelegate, times(1)).instructions() - verify(mockDelegate, times(1)).metadata() - verify(mockDelegate, times(1)).model() - verify(mockDelegate, times(1))._object_() verify(mockDelegate, times(1))._output() // Indirect - verify(mockDelegate, times(1)).parallelToolCalls() - verify(mockDelegate, times(1)).temperature() - verify(mockDelegate, times(1)).toolChoice() - verify(mockDelegate, times(1)).tools() - verify(mockDelegate, times(1)).topP() - verify(mockDelegate, times(1)).maxOutputTokens() - verify(mockDelegate, times(1)).previousResponseId() - verify(mockDelegate, times(1)).reasoning() - verify(mockDelegate, times(1)).serviceTier() - verify(mockDelegate, times(1)).status() - verify(mockDelegate, times(1)).text() - verify(mockDelegate, times(1)).truncation() - verify(mockDelegate, times(1)).usage() - verify(mockDelegate, times(1)).user() + verify(mockDelegate, times(1)).validate() verifyNoMoreInteractions(mockDelegate) } } From f4b259cb70c0b32616eaa6b652557d2c678db95a Mon Sep 17 00:00:00 2001 From: Tomer Aberbach Date: Wed, 14 May 2025 13:02:10 -0400 Subject: [PATCH 17/17] docs: swap primary structured outputs example --- README.md | 2 +- .../StructuredOutputsClassExample.java | 72 ------------------- .../example/StructuredOutputsExample.java | 67 ++++++++++++----- ... => StructuredOutputsRawAsyncExample.java} | 5 +- .../example/StructuredOutputsRawExample.java | 42 +++++++++++ 5 files changed, 93 insertions(+), 95 deletions(-) delete mode 100644 openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java rename openai-java-example/src/main/java/com/openai/example/{StructuredOutputsAsyncExample.java => StructuredOutputsRawAsyncExample.java} (91%) create mode 100644 openai-java-example/src/main/java/com/openai/example/StructuredOutputsRawExample.java diff --git a/README.md b/README.md index 105aa4b98..dece1cd29 100644 --- a/README.md +++ b/README.md @@ -346,7 +346,7 @@ and setting it on the input parameters. However, for greater convenience, a JSON 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 -[`StructuredOutputsClassExample`](openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java). +[`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: diff --git a/openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java b/openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java deleted file mode 100644 index 3f65a9912..000000000 --- a/openai-java-example/src/main/java/com/openai/example/StructuredOutputsClassExample.java +++ /dev/null @@ -1,72 +0,0 @@ -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.chat.completions.ChatCompletionCreateParams; -import com.openai.models.chat.completions.StructuredChatCompletionCreateParams; -import java.util.List; - -public final class StructuredOutputsClassExample { - - 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 StructuredOutputsClassExample() {} - - 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(); - - StructuredChatCompletionCreateParams createParams = ChatCompletionCreateParams.builder() - .model(ChatModel.GPT_4O_MINI) - .maxCompletionTokens(2048) - .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()) - .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 9d3ce9daa..b4af99997 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 a645f6cb0..f726e0cfe 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 000000000..3b9ff03ac --- /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); + } +}