diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8d4c5a..b11acac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,10 +22,10 @@ jobs: if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Java - uses: actions/setup-java@v5 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: distribution: temurin java-version: | @@ -34,7 +34,7 @@ jobs: cache: gradle - name: Set up Gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3 - name: Run lints run: ./scripts/lint @@ -49,10 +49,10 @@ jobs: if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Java - uses: actions/setup-java@v5 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: distribution: temurin java-version: | @@ -61,7 +61,7 @@ jobs: cache: gradle - name: Set up Gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3 - name: Build SDK run: ./scripts/build @@ -71,7 +71,7 @@ jobs: github.repository == 'stainless-sdks/stagehand-java' && !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: core.setOutput('github_token', await core.getIDToken()); @@ -91,10 +91,10 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/stagehand-java' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Java - uses: actions/setup-java@v5 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: distribution: temurin java-version: | @@ -103,7 +103,7 @@ jobs: cache: gradle - name: Set up Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@a8f75513eafdebd8141bd1cd4e30fcd194af8dfa # v2.12.0 - name: Run tests run: ./scripts/test diff --git a/.github/workflows/publish-sonatype.yml b/.github/workflows/publish-sonatype.yml index e0e380c..ebab6f0 100644 --- a/.github/workflows/publish-sonatype.yml +++ b/.github/workflows/publish-sonatype.yml @@ -14,10 +14,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Java - uses: actions/setup-java@v5 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: distribution: temurin java-version: | @@ -26,7 +26,7 @@ jobs: cache: gradle - name: Set up Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@a8f75513eafdebd8141bd1cd4e30fcd194af8dfa # v2.12.0 - name: Publish to Sonatype run: |- diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index e71595c..4fcd85e 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -12,7 +12,7 @@ jobs: if: github.repository == 'browserbase/stagehand-java' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check release environment run: | diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d11c8fc..eba8a04 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.20.0" + ".": "3.21.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 391cde3..15099ca 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-6f6bfb81d092f30a5e2005328c97d61b9ea36132bb19e9e79e55294b9534ce20.yml -openapi_spec_hash: f3fc1e3688a38dc2c28f7178f7d534e5 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-c7910965e66e73ad8b65b6cc391d431094b2a6c6577c3e9d82feaa8138e74cff.yml +openapi_spec_hash: 37748bb69c22a9ce721d9b5a5861f964 config_hash: 1fb12ae9b478488bc1e56bfbdc210b01 diff --git a/CHANGELOG.md b/CHANGELOG.md index d94df07..1677dfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## 3.21.0 (2026-05-29) + +Full Changelog: [v3.20.0...v3.21.0](https://github.com/browserbase/stagehand-java/compare/v3.20.0...v3.21.0) + +### Features + +* [feat]: add `ignoreSelectors` to `observe()` ([aa7ad0c](https://github.com/browserbase/stagehand-java/commit/aa7ad0c2033478107f3944b625d1b172b8f39e83)) +* [STG-1756] forward Vertex model config ([b37b827](https://github.com/browserbase/stagehand-java/commit/b37b827f38cc168d12e0d3a355f25a137796f5d7)) +* Add `screenshot` option to Extract ([043d138](https://github.com/browserbase/stagehand-java/commit/043d138a98a8caab6bc2c59c2abb0a864019c3c5)) +* **client:** improve logging ([1d84566](https://github.com/browserbase/stagehand-java/commit/1d845661917baf3d12a97286cf760e0d0fda0218)) +* STG-1756 add Vertex auth params to Stagehand spec ([f072067](https://github.com/browserbase/stagehand-java/commit/f072067e7adef04886423e1232e6b7e42e5f8a28)) + + +### Bug Fixes + +* **java:** update model config examples ([991a894](https://github.com/browserbase/stagehand-java/commit/991a894f6c93371eaf18a5878a41a4e27928adc8)) + + +### Chores + +* redact api-key headers in debug logs ([ce7e1d2](https://github.com/browserbase/stagehand-java/commit/ce7e1d278d5f7a9ceda6bc3108ed8d097f9b313d)) + ## 3.20.0 (2026-05-06) Full Changelog: [v3.19.3...v3.20.0](https://github.com/browserbase/stagehand-java/compare/v3.19.3...v3.20.0) diff --git a/README.md b/README.md index 86c046b..b18062f 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ -[![Maven Central](https://img.shields.io/maven-central/v/com.browserbase.api/stagehand-java)](https://central.sonatype.com/artifact/com.browserbase.api/stagehand-java/3.20.0) -[![javadoc](https://javadoc.io/badge2/com.browserbase.api/stagehand-java/3.20.0/javadoc.svg)](https://javadoc.io/doc/com.browserbase.api/stagehand-java/3.20.0) +[![Maven Central](https://img.shields.io/maven-central/v/com.browserbase.api/stagehand-java)](https://central.sonatype.com/artifact/com.browserbase.api/stagehand-java/3.21.0) +[![javadoc](https://javadoc.io/badge2/com.browserbase.api/stagehand-java/3.21.0/javadoc.svg)](https://javadoc.io/doc/com.browserbase.api/stagehand-java/3.21.0) @@ -85,7 +85,7 @@ Most existing browser automation tools either require you to write low-level cod ### Gradle ```kotlin -implementation("com.browserbase.api:stagehand-java:3.20.0") +implementation("com.browserbase.api:stagehand-java:3.21.0") ``` ### Maven @@ -94,7 +94,7 @@ implementation("com.browserbase.api:stagehand-java:3.20.0") com.browserbase.api stagehand-java - 3.20.0 + 3.21.0 ``` @@ -526,8 +526,6 @@ The SDK throws custom unchecked exception types: ## Logging -The SDK uses the standard [OkHttp logging interceptor](https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor). - Enable logging by setting the `STAGEHAND_LOG` environment variable to `info`: ```sh @@ -540,6 +538,19 @@ Or to `debug` for more verbose logging: export STAGEHAND_LOG=debug ``` +Or configure the client manually using the `logLevel` method: + +```java +import com.browserbase.api.client.StagehandClient; +import com.browserbase.api.client.okhttp.StagehandOkHttpClient; +import com.browserbase.api.core.LogLevel; + +StagehandClient client = StagehandOkHttpClient.builder() + .fromEnv() + .logLevel(LogLevel.INFO) + .build(); +``` + ## ProGuard and R8 Although the SDK uses reflection, it is still usable with [ProGuard](https://github.com/Guardsquare/proguard) and [R8](https://developer.android.com/topic/performance/app-optimization/enable-app-optimization) because `stagehand-java-core` is published with a [configuration file](stagehand-java-core/src/main/resources/META-INF/proguard/stagehand-java-core.pro) containing [keep rules](https://www.guardsquare.com/manual/configuration/usage). diff --git a/build.gradle.kts b/build.gradle.kts index 4602d16..2f55137 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ repositories { allprojects { group = "com.browserbase.api" - version = "3.20.0" // x-release-please-version + version = "3.21.0" // x-release-please-version } subprojects { diff --git a/stagehand-java-client-okhttp/build.gradle.kts b/stagehand-java-client-okhttp/build.gradle.kts index 49e7e71..be765aa 100644 --- a/stagehand-java-client-okhttp/build.gradle.kts +++ b/stagehand-java-client-okhttp/build.gradle.kts @@ -7,7 +7,6 @@ dependencies { api(project(":stagehand-java-core")) implementation("com.squareup.okhttp3:okhttp:4.12.0") - implementation("com.squareup.okhttp3:logging-interceptor:4.12.0") testImplementation(kotlin("test")) testImplementation("org.assertj:assertj-core:3.27.7") diff --git a/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/OkHttpClient.kt b/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/OkHttpClient.kt index 2d3b42f..155ae2b 100644 --- a/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/OkHttpClient.kt +++ b/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/OkHttpClient.kt @@ -35,7 +35,6 @@ import okhttp3.Request import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response -import okhttp3.logging.HttpLoggingInterceptor import okio.BufferedSink import okio.buffer import okio.sink @@ -93,21 +92,6 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie private fun newCall(request: HttpRequest, requestOptions: RequestOptions): Call { val clientBuilder = okHttpClient.newBuilder() - val logLevel = - when (System.getenv("STAGEHAND_LOG")?.lowercase()) { - "info" -> HttpLoggingInterceptor.Level.BASIC - "debug" -> HttpLoggingInterceptor.Level.BODY - else -> null - } - if (logLevel != null) { - clientBuilder.addNetworkInterceptor( - HttpLoggingInterceptor().setLevel(logLevel).apply { - redactHeader("x-bb-api-key") - redactHeader("x-model-api-key") - } - ) - } - requestOptions.timeout?.let { clientBuilder .connectTimeout(it.connect()) diff --git a/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClient.kt b/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClient.kt index 972d3e3..36e0fe3 100644 --- a/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClient.kt +++ b/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClient.kt @@ -5,6 +5,7 @@ package com.browserbase.api.client.okhttp import com.browserbase.api.client.StagehandClient import com.browserbase.api.client.StagehandClientImpl import com.browserbase.api.core.ClientOptions +import com.browserbase.api.core.LogLevel import com.browserbase.api.core.Sleeper import com.browserbase.api.core.Timeout import com.browserbase.api.core.http.AsyncStreamResponse @@ -290,6 +291,15 @@ class StagehandOkHttpClient private constructor() { */ fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) } + /** + * The level at which to log request and response information. + * + * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv]. + * + * Defaults to [LogLevel.fromEnv]. + */ + fun logLevel(logLevel: LogLevel) = apply { clientOptions.logLevel(logLevel) } + /** Your [Browserbase API Key](https://www.browserbase.com/settings) */ fun browserbaseApiKey(browserbaseApiKey: String) = apply { clientOptions.browserbaseApiKey(browserbaseApiKey) diff --git a/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClientAsync.kt b/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClientAsync.kt index 321443a..83bb57e 100644 --- a/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClientAsync.kt +++ b/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClientAsync.kt @@ -5,6 +5,7 @@ package com.browserbase.api.client.okhttp import com.browserbase.api.client.StagehandClientAsync import com.browserbase.api.client.StagehandClientAsyncImpl import com.browserbase.api.core.ClientOptions +import com.browserbase.api.core.LogLevel import com.browserbase.api.core.Sleeper import com.browserbase.api.core.Timeout import com.browserbase.api.core.http.AsyncStreamResponse @@ -290,6 +291,15 @@ class StagehandOkHttpClientAsync private constructor() { */ fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) } + /** + * The level at which to log request and response information. + * + * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv]. + * + * Defaults to [LogLevel.fromEnv]. + */ + fun logLevel(logLevel: LogLevel) = apply { clientOptions.logLevel(logLevel) } + /** Your [Browserbase API Key](https://www.browserbase.com/settings) */ fun browserbaseApiKey(browserbaseApiKey: String) = apply { clientOptions.browserbaseApiKey(browserbaseApiKey) diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/ClientOptions.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/ClientOptions.kt index 90fa08b..1f88c0a 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/ClientOptions.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/ClientOptions.kt @@ -5,6 +5,7 @@ package com.browserbase.api.core import com.browserbase.api.core.http.AsyncStreamResponse import com.browserbase.api.core.http.Headers import com.browserbase.api.core.http.HttpClient +import com.browserbase.api.core.http.LoggingHttpClient import com.browserbase.api.core.http.PhantomReachableClosingHttpClient import com.browserbase.api.core.http.QueryParams import com.browserbase.api.core.http.RetryingHttpClient @@ -110,6 +111,14 @@ private constructor( * Defaults to 2. */ @get:JvmName("maxRetries") val maxRetries: Int, + /** + * The level at which to log request and response information. + * + * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv]. + * + * Defaults to [LogLevel.fromEnv]. + */ + @get:JvmName("logLevel") val logLevel: LogLevel, /** Your [Browserbase API Key](https://www.browserbase.com/settings) */ @get:JvmName("browserbaseApiKey") val browserbaseApiKey: String, private val browserbaseProjectId: String?, @@ -176,6 +185,7 @@ private constructor( private var responseValidation: Boolean = false private var timeout: Timeout = Timeout.default() private var maxRetries: Int = 2 + private var logLevel: LogLevel = LogLevel.fromEnv() private var browserbaseApiKey: String? = null private var browserbaseProjectId: String? = null private var modelApiKey: String? = null @@ -194,6 +204,7 @@ private constructor( responseValidation = clientOptions.responseValidation timeout = clientOptions.timeout maxRetries = clientOptions.maxRetries + logLevel = clientOptions.logLevel browserbaseApiKey = clientOptions.browserbaseApiKey browserbaseProjectId = clientOptions.browserbaseProjectId modelApiKey = clientOptions.modelApiKey @@ -320,6 +331,15 @@ private constructor( */ fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries } + /** + * The level at which to log request and response information. + * + * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv]. + * + * Defaults to [LogLevel.fromEnv]. + */ + fun logLevel(logLevel: LogLevel) = apply { this.logLevel = logLevel } + /** Your [Browserbase API Key](https://www.browserbase.com/settings) */ fun browserbaseApiKey(browserbaseApiKey: String) = apply { this.browserbaseApiKey = browserbaseApiKey @@ -327,7 +347,7 @@ private constructor( /** * Deprecated. Browserbase API keys are now project-scoped, so this value is no longer - * required. Accepted for backwards compatibility; it is ignored. + * required. */ fun browserbaseProjectId(browserbaseProjectId: String?) = apply { this.browserbaseProjectId = browserbaseProjectId @@ -430,23 +450,28 @@ private constructor( * * See this table for the available options: * - * |Setter |System property |Environment variable |Required|Default value | - * |-------------------|-----------------------------|---------------------|--------|-----------------------------------------| - * |`browserbaseApiKey`|`stagehand.browserbaseApiKey`|`BROWSERBASE_API_KEY`|true |- | - * |`modelApiKey` |`stagehand.modelApiKey` |`MODEL_API_KEY` |true |- | - * |`baseUrl` |`stagehand.baseUrl` |`STAGEHAND_API_URL` |false |`"https://api.stagehand.browserbase.com"`| + * |Setter |System property |Environment variable |Required|Default value | + * |----------------------|--------------------------------|------------------------|--------|-----------------------------------------| + * |`browserbaseApiKey` |`stagehand.browserbaseApiKey` |`BROWSERBASE_API_KEY` |true |- | + * |`browserbaseProjectId`|`stagehand.browserbaseProjectId`|`BROWSERBASE_PROJECT_ID`|false |- | + * |`modelApiKey` |`stagehand.modelApiKey` |`MODEL_API_KEY` |true |- | + * |`baseUrl` |`stagehand.baseUrl` |`STAGEHAND_BASE_URL` |true |`"https://api.stagehand.browserbase.com"`| * * System properties take precedence over environment variables. */ fun fromEnv() = fromEnv(System::getenv) - internal fun fromEnv(getEnv: (String) -> String?) = apply { + fun fromEnv(getEnv: (String) -> String?) = apply { + logLevel(LogLevel.fromEnv()) (System.getProperty("stagehand.baseUrl") ?: getEnv("STAGEHAND_API_URL") ?: getEnv("STAGEHAND_BASE_URL")) ?.let { baseUrl(it) } (System.getProperty("stagehand.browserbaseApiKey") ?: getEnv("BROWSERBASE_API_KEY")) ?.let { browserbaseApiKey(it) } + (System.getProperty("stagehand.browserbaseProjectId") + ?: getEnv("BROWSERBASE_PROJECT_ID")) + ?.let { browserbaseProjectId(it) } (System.getProperty("stagehand.modelApiKey") ?: getEnv("MODEL_API_KEY"))?.let { modelApiKey(it) } @@ -525,7 +550,13 @@ private constructor( return ClientOptions( httpClient, RetryingHttpClient.builder() - .httpClient(httpClient) + .httpClient( + LoggingHttpClient.builder() + .httpClient(httpClient) + .clock(clock) + .level(logLevel) + .build() + ) .sleeper(sleeper) .clock(clock) .maxRetries(maxRetries) @@ -541,6 +572,7 @@ private constructor( responseValidation, timeout, maxRetries, + logLevel, browserbaseApiKey, browserbaseProjectId, modelApiKey, diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/LogLevel.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/LogLevel.kt new file mode 100644 index 0000000..f5a07a1 --- /dev/null +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/LogLevel.kt @@ -0,0 +1,33 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.browserbase.api.core + +/** The level at which to log request and response information. */ +enum class LogLevel { + /** No logging. */ + OFF, + /** Minimal request and response summary logs. No headers or bodies are logged. */ + INFO, + /** [INFO] logs plus details about request failures. */ + ERROR, + /** + * Full request and response logs. Sensitive headers are redacted, but sensitive data in request + * and response bodies may still be visible. + */ + DEBUG; + + /** Returns whether this level is at or higher than the given [level]. */ + fun shouldLog(level: LogLevel): Boolean = ordinal >= level.ordinal + + companion object { + + /** Returns a [LogLevel] based on the `STAGEHAND_LOG` environment variable. */ + fun fromEnv() = + when (System.getenv("STAGEHAND_LOG")?.lowercase()) { + "info" -> INFO + "error" -> ERROR + "debug" -> DEBUG + else -> OFF + } + } +} diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/Utils.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/Utils.kt index c24f648..ee3da60 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/Utils.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/Utils.kt @@ -5,6 +5,7 @@ package com.browserbase.api.core import com.browserbase.api.errors.StagehandInvalidDataException import java.util.Collections import java.util.SortedMap +import java.util.SortedSet import java.util.concurrent.CompletableFuture import java.util.concurrent.locks.Lock @@ -16,6 +17,11 @@ internal fun T?.getOrThrow(name: String): T = internal fun List.toImmutable(): List = if (isEmpty()) Collections.emptyList() else Collections.unmodifiableList(toList()) +@JvmSynthetic +internal fun > SortedSet.toImmutable(): SortedSet = + if (isEmpty()) Collections.emptySortedSet() + else Collections.unmodifiableSortedSet(toSortedSet(comparator() ?: Comparator.naturalOrder())) + @JvmSynthetic internal fun Map.toImmutable(): Map = if (isEmpty()) immutableEmptyMap() else Collections.unmodifiableMap(toMap()) diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/http/LoggingHttpClient.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/http/LoggingHttpClient.kt new file mode 100644 index 0000000..564bbfc --- /dev/null +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/http/LoggingHttpClient.kt @@ -0,0 +1,639 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.browserbase.api.core.http + +import com.browserbase.api.core.LogLevel +import com.browserbase.api.core.RequestOptions +import com.browserbase.api.core.checkRequired +import com.browserbase.api.core.toImmutable +import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.io.OutputStream +import java.nio.ByteBuffer +import java.nio.charset.CharacterCodingException +import java.nio.charset.Charset +import java.nio.charset.CharsetDecoder +import java.nio.charset.CodingErrorAction +import java.nio.charset.StandardCharsets +import java.time.Clock +import java.time.Duration +import java.time.OffsetDateTime +import java.util.SortedSet +import java.util.concurrent.CompletableFuture +import java.util.concurrent.CompletionException +import kotlin.time.toKotlinDuration + +/** A wrapper [HttpClient] around [httpClient] that logs request and response information. */ +class LoggingHttpClient +private constructor( + /** The underlying [HttpClient] for making requests. */ + @get:JvmName("httpClient") val httpClient: HttpClient, + /** + * Sensitive headers to redact from logs. + * + * Defaults to `Set.of("authorization", "api-key", "x-api-key", "cookie", "set-cookie", + * "x-bb-api-key", "x-bb-project-id", "x-model-api-key")`. + */ + @get:JvmName("redactedHeaders") val redactedHeaders: SortedSet, + /** + * The clock to use for measuring request and response durations. + * + * This is primarily useful for using a fake clock in tests. + * + * Defaults to [Clock.systemUTC]. + */ + @get:JvmName("clock") val clock: Clock, + /** + * The log level to use. + * + * Pass [LogLevel.fromEnv] to read from environment variables. + */ + @get:JvmName("level") val level: LogLevel, +) : HttpClient { + + override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse { + val loggingRequest = logRequest(request) + + val before = OffsetDateTime.now(clock) + val response = + try { + httpClient.execute(loggingRequest, requestOptions) + } catch (e: Throwable) { + logFailure(e, Duration.between(before, OffsetDateTime.now(clock))) + throw e + } + + val took = Duration.between(before, OffsetDateTime.now(clock)) + return logResponse(response, took) + } + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture { + val loggingRequest = logRequest(request) + + val before = OffsetDateTime.now(clock) + val future = + try { + httpClient.executeAsync(loggingRequest, requestOptions) + } catch (e: Throwable) { + logFailure(e, Duration.between(before, OffsetDateTime.now(clock))) + throw e + } + return future.handle { response, error -> + val took = Duration.between(before, OffsetDateTime.now(clock)) + if (error != null) { + logFailure(unwrapCompletionException(error), took) + throw error + } + logResponse(response, took) + } + } + + private fun logRequest(request: HttpRequest): HttpRequest { + if (!level.shouldLog(LogLevel.INFO)) { + return request + } + + System.err.println( + buildString { + append("--> ${request.method} ${request.url()}") + request.body?.let { + val length = it.contentLength() + append(if (length >= 0) " ($length-byte body)" else " (unknown-length body)") + } + } + ) + + if (!level.shouldLog(LogLevel.DEBUG)) { + return request + } + + logHeaders(request.headers) + + if (request.body == null) { + System.err.println("--> END ${request.method}") + System.err.println() + return request + } + + return request + .toBuilder() + .body(LoggingHttpRequestBody(request.method, request.body)) + .build() + } + + private fun logResponse(response: HttpResponse, took: Duration): HttpResponse { + if (!level.shouldLog(LogLevel.INFO)) { + return response + } + + val contentLength = response.headers().values("Content-Length").firstOrNull()?.toIntOrNull() + System.err.println( + "<-- ${response.statusCode()} (${ + buildString { + append(took.format()) + contentLength?.let { append(", $contentLength-byte body") } + } + })" + ) + + if (!level.shouldLog(LogLevel.DEBUG)) { + return response + } + + logHeaders(response.headers()) + return LoggingHttpResponse(response) + } + + private fun logFailure(error: Throwable, took: Duration) { + if (!level.shouldLog(LogLevel.ERROR)) { + return + } + + System.err.println( + buildString { + append("<-- !! ${error.javaClass.simpleName}") + error.message?.let { append(": $it") } + append(" (${took.format()})") + } + ) + } + + private fun unwrapCompletionException(error: Throwable): Throwable = + if (error is CompletionException && error.cause != null) error.cause!! else error + + private fun logHeaders(headers: Headers) = + headers.names().forEach { name -> + headers.values(name).forEach { value -> + System.err.println("$name: ${if (redactedHeaders.contains(name)) "██" else value}") + } + } + + override fun close() = httpClient.close() + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [LoggingHttpClient]. + * + * The following fields are required: + * ```java + * .httpClient() + * .level() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [LoggingHttpClient]. */ + class Builder internal constructor() { + + private var httpClient: HttpClient? = null + private var redactedHeaders: Set = + setOf( + "authorization", + "api-key", + "x-api-key", + "cookie", + "set-cookie", + "x-bb-api-key", + "x-bb-project-id", + "x-model-api-key", + ) + private var clock: Clock = Clock.systemUTC() + private var level: LogLevel? = null + + @JvmSynthetic + internal fun from(loggingHttpClient: LoggingHttpClient) = apply { + httpClient = loggingHttpClient.httpClient + redactedHeaders = loggingHttpClient.redactedHeaders + clock = loggingHttpClient.clock + level = loggingHttpClient.level + } + + /** The underlying [HttpClient] for making requests. */ + fun httpClient(httpClient: HttpClient) = apply { this.httpClient = httpClient } + + /** + * Sensitive headers to redact from logs. + * + * Defaults to `Set.of("authorization", "api-key", "x-api-key", "cookie", "set-cookie", + * "x-bb-api-key", "x-bb-project-id", "x-model-api-key")`. + */ + fun redactedHeaders(redactedHeaders: Set) = apply { + this.redactedHeaders = redactedHeaders + } + + /** + * The clock to use for measuring request and response durations. + * + * This is primarily useful for using a fake clock in tests. + * + * Defaults to [Clock.systemUTC]. + */ + fun clock(clock: Clock) = apply { this.clock = clock } + + /** + * The log level to use. + * + * Pass [LogLevel.fromEnv] to read from environment variables. + */ + fun level(level: LogLevel) = apply { this.level = level } + + /** + * Returns an immutable instance of [LoggingHttpClient]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .httpClient() + * .level() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): LoggingHttpClient = + LoggingHttpClient( + checkRequired("httpClient", httpClient), + redactedHeaders.toSortedSet(String.CASE_INSENSITIVE_ORDER).toImmutable(), + clock, + checkRequired("level", level), + ) + } +} + +/** + * An [HttpRequestBody] wrapper that delegates to [body] while also logging line by line as it's + * written. + * + * The logging occurs in a streaming manner with minimal buffering. + */ +private class LoggingHttpRequestBody( + private val method: HttpMethod, + private val body: HttpRequestBody, +) : HttpRequestBody { + + private val charset by lazy { parseCharset(body.contentType()) } + + override fun writeTo(outputStream: OutputStream) { + val loggingOutputStream = LoggingOutputStream(outputStream, charset) + body.writeTo(loggingOutputStream) + + loggingOutputStream.flush() + System.err.println("--> END $method (${loggingOutputStream.writeCount()}-byte body)") + System.err.println() + } + + override fun contentType(): String? = body.contentType() + + override fun contentLength(): Long = body.contentLength() + + override fun repeatable(): Boolean = body.repeatable() + + override fun close() = body.close() +} + +/** + * An [OutputStream] wrapper that delegates to [outputStream] while also logging bytes line by line + * as it's written to. + * + * The written content is assumed to be in the given [charset] and the logging occurs in a streaming + * manner with minimal buffering. + */ +private class LoggingOutputStream(private val outputStream: OutputStream, charset: Charset?) : + OutputStream() { + + private val buffer = LoggingBuffer(charset) + + fun writeCount() = buffer.writeCount() + + override fun write(b: Int) { + outputStream.write(b) + buffer.write(b) + } + + override fun write(b: ByteArray, off: Int, len: Int) { + outputStream.write(b, off, len) + for (i in off until off + len) { + buffer.write(b[i].toInt() and 0xFF) + } + } + + /** Prints any currently buffered content. */ + override fun flush() { + buffer.flush() + outputStream.flush() + } + + override fun close() = outputStream.close() +} + +/** + * An [HttpResponse] wrapper that delegates to [response] while also logging line-by-line as it's + * read. + * + * The logging occurs in a streaming manner with minimal buffering. + */ +private class LoggingHttpResponse(private val response: HttpResponse) : HttpResponse { + + private val loggingBody: Lazy = lazy { + LoggingInputStream( + response.body(), + parseCharset(response.headers().values("Content-Type").firstOrNull()), + ) + } + + override fun statusCode(): Int = response.statusCode() + + override fun headers(): Headers = response.headers() + + override fun body(): InputStream = loggingBody.value + + override fun close() { + if (loggingBody.isInitialized()) { + loggingBody.value.close() + } + response.close() + } +} + +/** + * An [InputStream] wrapper that delegates to [inputStream] while also logging bytes line by line as + * it's read. + * + * The contents of [inputStream] are assumed to be in the given [charset] and the logging occurs in + * a streaming manner with minimal buffering. + */ +private class LoggingInputStream(private val inputStream: InputStream, charset: Charset?) : + InputStream() { + + private var isDone = false + private val buffer = LoggingBuffer(charset) + + override fun read(): Int { + if (isDone) { + return -1 + } + + val b = inputStream.read() + + if (b == -1) { + markDone() + return b + } + + buffer.write(b) + return b + } + + override fun read(b: ByteArray, off: Int, len: Int): Int { + if (isDone) { + return -1 + } + + val bytesRead = inputStream.read(b, off, len) + + if (bytesRead == -1) { + markDone() + return bytesRead + } + + for (i in off until off + bytesRead) { + buffer.write(b[i].toInt() and 0xFF) + } + return bytesRead + } + + override fun close() { + if (!isDone) { + markDone(closedEarly = true) + } + inputStream.close() + } + + private fun markDone(closedEarly: Boolean = false) { + isDone = true + buffer.flush() + val suffix = if (closedEarly) ", closed early" else "" + System.err.println("<-- END HTTP (${buffer.writeCount()}-byte body$suffix)") + System.err.println() + } +} + +/** + * A byte buffer that prints line by line, using the given [charset], as bytes are written to it. + * + * When [charset] is `null`, the buffer performs an upfront check to detect binary content. If + * non-whitespace ISO control characters are found in the first [PROBABLY_UTF8_CODE_POINT_LIMIT] + * code points, body logging is suppressed entirely. + */ +private class LoggingBuffer(charset: Charset?) { + + private val charset = charset ?: StandardCharsets.UTF_8 + + private val decoder: CharsetDecoder = + this.charset + .newDecoder() + .onMalformedInput(CodingErrorAction.REPORT) + .onUnmappableCharacter(CodingErrorAction.REPORT) + private var writeCount = 0 + private val buffer = ByteArrayOutputStream(128) + + /** + * Whether logging has been suppressed because the content doesn't appear to be readable text. + * + * This is only set when [charset] is `null` and the content fails the [isProbablyUtf8] check. + */ + private var suppressed = false + + /** + * Bytes accumulated for the [isProbablyUtf8] check before any lines are printed. + * + * Once the check passes (or [charset] is non-null), this is set to `null` and bytes flow + * directly to [buffer]. + */ + private var prefetchBuffer: ByteArrayOutputStream? = + if (charset != null) null else ByteArrayOutputStream(128) + + fun writeCount() = writeCount + + fun write(b: Int) { + if (writeCount == 0) { + // Print a newline before we start printing anything to separate the printed content + // from previous content. + System.err.println() + } + + writeCount++ + + if (suppressed) { + return + } + + val prefetch = prefetchBuffer + if (prefetch != null) { + prefetch.write(b) + // Continue accumulating until we have enough bytes to decide. + if (prefetch.size() < PROBABLY_UTF8_BYTE_LIMIT && b != '\n'.code) { + return + } + // We have enough bytes. Check if the content is probably UTF-8. + prefetchBuffer = null + val bytes = prefetch.toByteArray() + if (!isProbablyUtf8(bytes)) { + suppressed = true + System.err.println("(binary body omitted)") + return + } + // Content looks like UTF-8. Feed the accumulated bytes into the normal buffer. + for (byte in bytes) { + writeToBuffer(byte.toInt() and 0xFF) + } + return + } + + writeToBuffer(b) + } + + private fun writeToBuffer(b: Int) { + if (b == '\n'.code) { + flush() + return + } + + buffer.write(b) + } + + /** Prints any currently buffered content. */ + fun flush() { + if (suppressed) { + return + } + + // If we still have a prefetch buffer when flush is called (body was shorter than the + // limit), run the check now. + val prefetch = prefetchBuffer + if (prefetch != null) { + prefetchBuffer = null + val bytes = prefetch.toByteArray() + if (bytes.isEmpty()) { + return + } + if (!isProbablyUtf8(bytes)) { + suppressed = true + System.err.println("(binary body omitted)") + return + } + for (byte in bytes) { + writeToBuffer(byte.toInt() and 0xFF) + } + } + + if (buffer.size() == 0) { + return + } + + val line = + try { + decoder.decode(ByteBuffer.wrap(buffer.toByteArray())) + } catch (e: CharacterCodingException) { + "(omitted line is not valid $charset)" + } + buffer.reset() + System.err.println(line) + } +} + +/** The maximum number of code points to sample when checking if content is probably UTF-8. */ +private const val PROBABLY_UTF8_CODE_POINT_LIMIT = 64 + +/** + * The maximum number of bytes to accumulate before running the [isProbablyUtf8] check. UTF-8 code + * points are at most 4 bytes, so this accommodates [PROBABLY_UTF8_CODE_POINT_LIMIT] code points. + */ +private const val PROBABLY_UTF8_BYTE_LIMIT = PROBABLY_UTF8_CODE_POINT_LIMIT * 4 + +/** + * Returns `true` if the given [bytes] probably contain human-readable UTF-8 text. + * + * Decodes up to [PROBABLY_UTF8_CODE_POINT_LIMIT] code points and returns `false` if any + * non-whitespace ISO control characters are found, or if the bytes are not valid UTF-8. + */ +private fun isProbablyUtf8(bytes: ByteArray): Boolean { + try { + val decoder = + StandardCharsets.UTF_8.newDecoder() + .onMalformedInput(CodingErrorAction.REPORT) + .onUnmappableCharacter(CodingErrorAction.REPORT) + val charBuffer = decoder.decode(ByteBuffer.wrap(bytes)) + var codePointCount = 0 + var i = 0 + while (i < charBuffer.length && codePointCount < PROBABLY_UTF8_CODE_POINT_LIMIT) { + val codePoint = Character.codePointAt(charBuffer, i) + if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) { + return false + } + i += Character.charCount(codePoint) + codePointCount++ + } + return true + } catch (e: CharacterCodingException) { + return false + } +} + +/** Returns the [Charset] in the given [contentType] string, or `null` if unspecified. */ +private fun parseCharset(contentType: String?): Charset? = + contentType + ?.split(";") + ?.drop(1) + ?.map { it.trim() } + ?.firstOrNull { it.startsWith("charset=", ignoreCase = true) } + ?.substringAfter("=") + ?.trim() + ?.removeSurrounding("\"") + ?.let { runCatching { charset(it) }.getOrNull() } + +/** Formats the [Duration] into a string like "1m 40s 467ms". */ +private fun Duration.format(): String = + toKotlinDuration().toComponents { days, hours, minutes, seconds, nanoseconds -> + buildString { + val milliseconds = nanoseconds / 1_000_000 + if (days > 0) { + append("${days}d") + } + if (hours > 0) { + if (isNotEmpty()) { + append(" ") + } + append("${hours}h") + } + if (minutes > 0) { + if (isNotEmpty()) { + append(" ") + } + append("${minutes}m") + } + if (seconds > 0) { + if (isNotEmpty()) { + append(" ") + } + append("${seconds}s") + } + if (milliseconds > 0) { + if (isNotEmpty()) { + append(" ") + } + append("${milliseconds}ms") + } + + if (isEmpty()) { + append("0s") + } + } + } diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/ModelConfig.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/ModelConfig.kt index b20ac9d..b6cc034 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/ModelConfig.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/ModelConfig.kt @@ -2,336 +2,2897 @@ package com.browserbase.api.models.sessions +import com.browserbase.api.core.BaseDeserializer +import com.browserbase.api.core.BaseSerializer import com.browserbase.api.core.Enum import com.browserbase.api.core.ExcludeMissing import com.browserbase.api.core.JsonField import com.browserbase.api.core.JsonMissing import com.browserbase.api.core.JsonValue +import com.browserbase.api.core.allMaxBy import com.browserbase.api.core.checkRequired +import com.browserbase.api.core.getOrThrow import com.browserbase.api.core.toImmutable import com.browserbase.api.errors.StagehandInvalidDataException 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 java.util.Collections import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull +@JsonDeserialize(using = ModelConfig.Deserializer::class) +@JsonSerialize(using = ModelConfig.Serializer::class) class ModelConfig -@JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val modelName: JsonField, - private val apiKey: JsonField, - private val baseUrl: JsonField, - private val headers: JsonField, - private val provider: JsonField, - private val additionalProperties: MutableMap, + private val vertexModelConfigObject: VertexModelConfigObject? = null, + private val genericModelConfigObject: GenericModelConfigObject? = null, + private val _json: JsonValue? = null, ) { - @JsonCreator - private constructor( - @JsonProperty("modelName") @ExcludeMissing modelName: JsonField = JsonMissing.of(), - @JsonProperty("apiKey") @ExcludeMissing apiKey: JsonField = JsonMissing.of(), - @JsonProperty("baseURL") @ExcludeMissing baseUrl: JsonField = JsonMissing.of(), - @JsonProperty("headers") @ExcludeMissing headers: JsonField = JsonMissing.of(), - @JsonProperty("provider") @ExcludeMissing provider: JsonField = JsonMissing.of(), - ) : this(modelName, apiKey, baseUrl, headers, provider, mutableMapOf()) + fun vertexModelConfigObject(): Optional = + Optional.ofNullable(vertexModelConfigObject) - /** - * Model name string with provider prefix (e.g., 'openai/gpt-5-nano') - * - * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is - * unexpectedly missing or null (e.g. if the server responded with an unexpected value). - */ - fun modelName(): String = modelName.getRequired("modelName") + fun genericModelConfigObject(): Optional = + Optional.ofNullable(genericModelConfigObject) - /** - * API key for the model provider - * - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected value). - */ - fun apiKey(): Optional = apiKey.getOptional("apiKey") + fun isVertexModelConfigObject(): Boolean = vertexModelConfigObject != null - /** - * Base URL for the model provider - * - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected value). - */ - fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + fun isGenericModelConfigObject(): Boolean = genericModelConfigObject != null - /** - * Custom headers sent with every request to the model provider - * - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected value). - */ - fun headers(): Optional = headers.getOptional("headers") + fun asVertexModelConfigObject(): VertexModelConfigObject = + vertexModelConfigObject.getOrThrow("vertexModelConfigObject") + + fun asGenericModelConfigObject(): GenericModelConfigObject = + genericModelConfigObject.getOrThrow("genericModelConfigObject") + + fun _json(): Optional = Optional.ofNullable(_json) /** - * AI provider for the model (or provide a baseURL endpoint instead) + * Maps this instance's current variant to a value of type [T] using the given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of the + * SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.browserbase.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = modelConfig.accept(new ModelConfig.Visitor>() { + * @Override + * public Optional visitVertexModelConfigObject(VertexModelConfigObject vertexModelConfigObject) { + * return Optional.of(vertexModelConfigObject.toString()); + * } * - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected value). + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws StagehandInvalidDataException if [Visitor.unknown] is not overridden in [visitor] and + * the current variant is unknown. */ - fun provider(): Optional = provider.getOptional("provider") + fun accept(visitor: Visitor): T = + when { + vertexModelConfigObject != null -> + visitor.visitVertexModelConfigObject(vertexModelConfigObject) + genericModelConfigObject != null -> + visitor.visitGenericModelConfigObject(genericModelConfigObject) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false /** - * Returns the raw JSON value of [modelName]. + * Validates that the types of all values in this object match their expected types recursively. * - * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected type. + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't match its + * expected type. */ - @JsonProperty("modelName") @ExcludeMissing fun _modelName(): JsonField = modelName + fun validate(): ModelConfig = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ) { + vertexModelConfigObject.validate() + } + + override fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ) { + genericModelConfigObject.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + false + } /** - * Returns the raw JSON value of [apiKey]. + * Returns a score indicating how many valid values are contained in this object recursively. * - * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected type. + * Used for best match union deserialization. */ - @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ) = vertexModelConfigObject.validity() + + override fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ) = genericModelConfigObject.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ModelConfig && + vertexModelConfigObject == other.vertexModelConfigObject && + genericModelConfigObject == other.genericModelConfigObject + } + + override fun hashCode(): Int = Objects.hash(vertexModelConfigObject, genericModelConfigObject) + + override fun toString(): String = + when { + vertexModelConfigObject != null -> + "ModelConfig{vertexModelConfigObject=$vertexModelConfigObject}" + genericModelConfigObject != null -> + "ModelConfig{genericModelConfigObject=$genericModelConfigObject}" + _json != null -> "ModelConfig{_unknown=$_json}" + else -> throw IllegalStateException("Invalid ModelConfig") + } + + companion object { + + @JvmStatic + fun ofVertexModelConfigObject(vertexModelConfigObject: VertexModelConfigObject) = + ModelConfig(vertexModelConfigObject = vertexModelConfigObject) + + @JvmStatic + fun ofGenericModelConfigObject(genericModelConfigObject: GenericModelConfigObject) = + ModelConfig(genericModelConfigObject = genericModelConfigObject) + } /** - * Returns the raw JSON value of [baseUrl]. - * - * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected type. + * An interface that defines how to map each variant of [ModelConfig] to a value of type [T]. */ - @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + interface Visitor { + + fun visitVertexModelConfigObject(vertexModelConfigObject: VertexModelConfigObject): T + + fun visitGenericModelConfigObject(genericModelConfigObject: GenericModelConfigObject): T + + /** + * Maps an unknown variant of [ModelConfig] to a value of type [T]. + * + * An instance of [ModelConfig] 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 StagehandInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw StagehandInvalidDataException("Unknown ModelConfig: $json") + } + } + + internal class Deserializer : BaseDeserializer(ModelConfig::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): ModelConfig { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + ModelConfig(vertexModelConfigObject = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ModelConfig(genericModelConfigObject = 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 -> ModelConfig(_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(ModelConfig::class) { + + override fun serialize( + value: ModelConfig, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.vertexModelConfigObject != null -> + generator.writeObject(value.vertexModelConfigObject) + value.genericModelConfigObject != null -> + generator.writeObject(value.genericModelConfigObject) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid ModelConfig") + } + } + } + + class VertexModelConfigObject + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val auth: JsonField, + private val modelName: JsonField, + private val provider: JsonValue, + private val providerOptions: JsonField, + private val apiKey: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("auth") @ExcludeMissing auth: JsonField = JsonMissing.of(), + @JsonProperty("modelName") + @ExcludeMissing + modelName: JsonField = JsonMissing.of(), + @JsonProperty("provider") @ExcludeMissing provider: JsonValue = JsonMissing.of(), + @JsonProperty("providerOptions") + @ExcludeMissing + providerOptions: JsonField = JsonMissing.of(), + @JsonProperty("apiKey") @ExcludeMissing apiKey: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") @ExcludeMissing baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") @ExcludeMissing headers: JsonField = JsonMissing.of(), + ) : this( + auth, + modelName, + provider, + providerOptions, + apiKey, + baseUrl, + headers, + mutableMapOf(), + ) + + /** + * Vertex provider authentication configuration + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun auth(): Auth = auth.getRequired("auth") + + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun modelName(): String = modelName.getRequired("modelName") + + /** + * Vertex AI model provider + * + * Expected to always return the following: + * ```java + * JsonValue.from("vertex") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonValue = provider + + /** + * Vertex provider-specific model configuration + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun providerOptions(): ProviderOptions = providerOptions.getRequired("providerOptions") + + /** + * API key for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun apiKey(): Optional = apiKey.getOptional("apiKey") + + /** + * Base URL for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * Returns the raw JSON value of [auth]. + * + * Unlike [auth], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("auth") @ExcludeMissing fun _auth(): JsonField = auth + + /** + * Returns the raw JSON value of [modelName]. + * + * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("modelName") @ExcludeMissing fun _modelName(): JsonField = modelName + + /** + * Returns the raw JSON value of [providerOptions]. + * + * Unlike [providerOptions], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("providerOptions") + @ExcludeMissing + fun _providerOptions(): JsonField = providerOptions + + /** + * Returns the raw JSON value of [apiKey]. + * + * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("headers") @ExcludeMissing fun _headers(): JsonField = headers + + @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 [VertexModelConfigObject]. + * + * The following fields are required: + * ```java + * .auth() + * .modelName() + * .providerOptions() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [VertexModelConfigObject]. */ + class Builder internal constructor() { + + private var auth: JsonField? = null + private var modelName: JsonField? = null + private var provider: JsonValue = JsonValue.from("vertex") + private var providerOptions: JsonField? = null + private var apiKey: JsonField = JsonMissing.of() + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(vertexModelConfigObject: VertexModelConfigObject) = apply { + auth = vertexModelConfigObject.auth + modelName = vertexModelConfigObject.modelName + provider = vertexModelConfigObject.provider + providerOptions = vertexModelConfigObject.providerOptions + apiKey = vertexModelConfigObject.apiKey + baseUrl = vertexModelConfigObject.baseUrl + headers = vertexModelConfigObject.headers + additionalProperties = vertexModelConfigObject.additionalProperties.toMutableMap() + } + + /** Vertex provider authentication configuration */ + fun auth(auth: Auth) = auth(JsonField.of(auth)) + + /** + * Sets [Builder.auth] to an arbitrary JSON value. + * + * You should usually call [Builder.auth] with a well-typed [Auth] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun auth(auth: JsonField) = apply { this.auth = auth } + + /** Model name string with provider prefix (e.g., 'openai/gpt-5-nano') */ + fun modelName(modelName: String) = modelName(JsonField.of(modelName)) + + /** + * Sets [Builder.modelName] to an arbitrary JSON value. + * + * You should usually call [Builder.modelName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun modelName(modelName: JsonField) = apply { this.modelName = modelName } + + /** + * 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("vertex") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun provider(provider: JsonValue) = apply { this.provider = provider } + + /** Vertex provider-specific model configuration */ + fun providerOptions(providerOptions: ProviderOptions) = + providerOptions(JsonField.of(providerOptions)) + + /** + * Sets [Builder.providerOptions] to an arbitrary JSON value. + * + * You should usually call [Builder.providerOptions] with a well-typed [ProviderOptions] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun providerOptions(providerOptions: JsonField) = apply { + this.providerOptions = providerOptions + } + + /** API key for the model provider */ + fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + + /** + * Sets [Builder.apiKey] to an arbitrary JSON value. + * + * You should usually call [Builder.apiKey] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } + + /** Base URL for the model provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl } + + /** Custom headers sent with every request to the model provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun headers(headers: JsonField) = apply { this.headers = headers } + + 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 [VertexModelConfigObject]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .auth() + * .modelName() + * .providerOptions() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): VertexModelConfigObject = + VertexModelConfigObject( + checkRequired("auth", auth), + checkRequired("modelName", modelName), + provider, + checkRequired("providerOptions", providerOptions), + apiKey, + baseUrl, + headers, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): VertexModelConfigObject = apply { + if (validated) { + return@apply + } + + auth().validate() + modelName() + _provider().let { + if (it != JsonValue.from("vertex")) { + throw StagehandInvalidDataException("'provider' is invalid, received $it") + } + } + providerOptions().validate() + apiKey() + baseUrl() + headers().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = + (auth.asKnown().getOrNull()?.validity() ?: 0) + + (if (modelName.asKnown().isPresent) 1 else 0) + + provider.let { if (it == JsonValue.from("vertex")) 1 else 0 } + + (providerOptions.asKnown().getOrNull()?.validity() ?: 0) + + (if (apiKey.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + /** Vertex provider authentication configuration */ + class Auth + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val credentials: JsonField, + private val type: JsonValue, + private val projectId: JsonField, + private val scopes: JsonField, + private val universeDomain: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("credentials") + @ExcludeMissing + credentials: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("projectId") + @ExcludeMissing + projectId: JsonField = JsonMissing.of(), + @JsonProperty("scopes") + @ExcludeMissing + scopes: JsonField = JsonMissing.of(), + @JsonProperty("universeDomain") + @ExcludeMissing + universeDomain: JsonField = JsonMissing.of(), + ) : this(credentials, type, projectId, scopes, universeDomain, mutableMapOf()) + + /** + * Google Cloud service account credentials + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun credentials(): Credentials = credentials.getRequired("credentials") + + /** + * Use inline Google Cloud service account credentials for provider authentication + * + * Expected to always return the following: + * ```java + * JsonValue.from("googleServiceAccount") + * ``` + * + * 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 + + /** + * Google Cloud project ID used by google-auth-library + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun projectId(): Optional = projectId.getOptional("projectId") + + /** + * Google auth scopes for the desired API request + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun scopes(): Optional = scopes.getOptional("scopes") + + /** + * Google Cloud universe domain + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun universeDomain(): Optional = universeDomain.getOptional("universeDomain") + + /** + * Returns the raw JSON value of [credentials]. + * + * Unlike [credentials], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("credentials") + @ExcludeMissing + fun _credentials(): JsonField = credentials + + /** + * Returns the raw JSON value of [projectId]. + * + * Unlike [projectId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("projectId") + @ExcludeMissing + fun _projectId(): JsonField = projectId + + /** + * Returns the raw JSON value of [scopes]. + * + * Unlike [scopes], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("scopes") @ExcludeMissing fun _scopes(): JsonField = scopes + + /** + * Returns the raw JSON value of [universeDomain]. + * + * Unlike [universeDomain], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("universeDomain") + @ExcludeMissing + fun _universeDomain(): JsonField = universeDomain + + @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 [Auth]. + * + * The following fields are required: + * ```java + * .credentials() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Auth]. */ + class Builder internal constructor() { + + private var credentials: JsonField? = null + private var type: JsonValue = JsonValue.from("googleServiceAccount") + private var projectId: JsonField = JsonMissing.of() + private var scopes: JsonField = JsonMissing.of() + private var universeDomain: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(auth: Auth) = apply { + credentials = auth.credentials + type = auth.type + projectId = auth.projectId + scopes = auth.scopes + universeDomain = auth.universeDomain + additionalProperties = auth.additionalProperties.toMutableMap() + } + + /** Google Cloud service account credentials */ + fun credentials(credentials: Credentials) = credentials(JsonField.of(credentials)) + + /** + * Sets [Builder.credentials] to an arbitrary JSON value. + * + * You should usually call [Builder.credentials] with a well-typed [Credentials] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun credentials(credentials: JsonField) = apply { + this.credentials = credentials + } + + /** + * 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("googleServiceAccount") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** Google Cloud project ID used by google-auth-library */ + fun projectId(projectId: String) = projectId(JsonField.of(projectId)) + + /** + * Sets [Builder.projectId] to an arbitrary JSON value. + * + * You should usually call [Builder.projectId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun projectId(projectId: JsonField) = apply { this.projectId = projectId } + + /** Google auth scopes for the desired API request */ + fun scopes(scopes: Scopes) = scopes(JsonField.of(scopes)) + + /** + * Sets [Builder.scopes] to an arbitrary JSON value. + * + * You should usually call [Builder.scopes] with a well-typed [Scopes] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun scopes(scopes: JsonField) = apply { this.scopes = scopes } + + /** Alias for calling [scopes] with `Scopes.ofString(string)`. */ + fun scopes(string: String) = scopes(Scopes.ofString(string)) + + /** Alias for calling [scopes] with `Scopes.ofStrings(strings)`. */ + fun scopesOfStrings(strings: List) = scopes(Scopes.ofStrings(strings)) + + /** Google Cloud universe domain */ + fun universeDomain(universeDomain: String) = + universeDomain(JsonField.of(universeDomain)) + + /** + * Sets [Builder.universeDomain] to an arbitrary JSON value. + * + * You should usually call [Builder.universeDomain] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun universeDomain(universeDomain: JsonField) = apply { + this.universeDomain = universeDomain + } + + 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 [Auth]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .credentials() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Auth = + Auth( + checkRequired("credentials", credentials), + type, + projectId, + scopes, + universeDomain, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Auth = apply { + if (validated) { + return@apply + } + + credentials().validate() + _type().let { + if (it != JsonValue.from("googleServiceAccount")) { + throw StagehandInvalidDataException("'type' is invalid, received $it") + } + } + projectId() + scopes().ifPresent { it.validate() } + universeDomain() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = + (credentials.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("googleServiceAccount")) 1 else 0 } + + (if (projectId.asKnown().isPresent) 1 else 0) + + (scopes.asKnown().getOrNull()?.validity() ?: 0) + + (if (universeDomain.asKnown().isPresent) 1 else 0) + + /** Google Cloud service account credentials */ + class Credentials + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val clientEmail: JsonField, + private val privateKey: JsonField, + private val authProviderX509CertUrl: JsonField, + private val authUri: JsonField, + private val clientId: JsonField, + private val clientX509CertUrl: JsonField, + private val privateKeyId: JsonField, + private val projectId: JsonField, + private val tokenUri: JsonField, + private val type: JsonField, + private val universeDomain: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("client_email") + @ExcludeMissing + clientEmail: JsonField = JsonMissing.of(), + @JsonProperty("private_key") + @ExcludeMissing + privateKey: JsonField = JsonMissing.of(), + @JsonProperty("auth_provider_x509_cert_url") + @ExcludeMissing + authProviderX509CertUrl: JsonField = JsonMissing.of(), + @JsonProperty("auth_uri") + @ExcludeMissing + authUri: JsonField = JsonMissing.of(), + @JsonProperty("client_id") + @ExcludeMissing + clientId: JsonField = JsonMissing.of(), + @JsonProperty("client_x509_cert_url") + @ExcludeMissing + clientX509CertUrl: JsonField = JsonMissing.of(), + @JsonProperty("private_key_id") + @ExcludeMissing + privateKeyId: JsonField = JsonMissing.of(), + @JsonProperty("project_id") + @ExcludeMissing + projectId: JsonField = JsonMissing.of(), + @JsonProperty("token_uri") + @ExcludeMissing + tokenUri: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + @JsonProperty("universe_domain") + @ExcludeMissing + universeDomain: JsonField = JsonMissing.of(), + ) : this( + clientEmail, + privateKey, + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + mutableMapOf(), + ) + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun clientEmail(): String = clientEmail.getRequired("client_email") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun privateKey(): String = privateKey.getRequired("private_key") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun authProviderX509CertUrl(): Optional = + authProviderX509CertUrl.getOptional("auth_provider_x509_cert_url") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun authUri(): Optional = authUri.getOptional("auth_uri") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun clientId(): Optional = clientId.getOptional("client_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun clientX509CertUrl(): Optional = + clientX509CertUrl.getOptional("client_x509_cert_url") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun privateKeyId(): Optional = privateKeyId.getOptional("private_key_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun projectId(): Optional = projectId.getOptional("project_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun tokenUri(): Optional = tokenUri.getOptional("token_uri") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun universeDomain(): Optional = + universeDomain.getOptional("universe_domain") + + /** + * Returns the raw JSON value of [clientEmail]. + * + * Unlike [clientEmail], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("client_email") + @ExcludeMissing + fun _clientEmail(): JsonField = clientEmail + + /** + * Returns the raw JSON value of [privateKey]. + * + * Unlike [privateKey], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("private_key") + @ExcludeMissing + fun _privateKey(): JsonField = privateKey + + /** + * Returns the raw JSON value of [authProviderX509CertUrl]. + * + * Unlike [authProviderX509CertUrl], this method doesn't throw if the JSON field has + * an unexpected type. + */ + @JsonProperty("auth_provider_x509_cert_url") + @ExcludeMissing + fun _authProviderX509CertUrl(): JsonField = authProviderX509CertUrl + + /** + * Returns the raw JSON value of [authUri]. + * + * Unlike [authUri], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("auth_uri") + @ExcludeMissing + fun _authUri(): JsonField = authUri + + /** + * Returns the raw JSON value of [clientId]. + * + * Unlike [clientId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("client_id") + @ExcludeMissing + fun _clientId(): JsonField = clientId + + /** + * Returns the raw JSON value of [clientX509CertUrl]. + * + * Unlike [clientX509CertUrl], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("client_x509_cert_url") + @ExcludeMissing + fun _clientX509CertUrl(): JsonField = clientX509CertUrl + + /** + * Returns the raw JSON value of [privateKeyId]. + * + * Unlike [privateKeyId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("private_key_id") + @ExcludeMissing + fun _privateKeyId(): JsonField = privateKeyId + + /** + * Returns the raw JSON value of [projectId]. + * + * Unlike [projectId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("project_id") + @ExcludeMissing + fun _projectId(): JsonField = projectId + + /** + * Returns the raw JSON value of [tokenUri]. + * + * Unlike [tokenUri], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("token_uri") + @ExcludeMissing + fun _tokenUri(): JsonField = tokenUri + + /** + * 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 [universeDomain]. + * + * Unlike [universeDomain], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("universe_domain") + @ExcludeMissing + fun _universeDomain(): JsonField = universeDomain + + @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 [Credentials]. + * + * The following fields are required: + * ```java + * .clientEmail() + * .privateKey() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Credentials]. */ + class Builder internal constructor() { + + private var clientEmail: JsonField? = null + private var privateKey: JsonField? = null + private var authProviderX509CertUrl: JsonField = JsonMissing.of() + private var authUri: JsonField = JsonMissing.of() + private var clientId: JsonField = JsonMissing.of() + private var clientX509CertUrl: JsonField = JsonMissing.of() + private var privateKeyId: JsonField = JsonMissing.of() + private var projectId: JsonField = JsonMissing.of() + private var tokenUri: JsonField = JsonMissing.of() + private var type: JsonField = JsonMissing.of() + private var universeDomain: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(credentials: Credentials) = apply { + clientEmail = credentials.clientEmail + privateKey = credentials.privateKey + authProviderX509CertUrl = credentials.authProviderX509CertUrl + authUri = credentials.authUri + clientId = credentials.clientId + clientX509CertUrl = credentials.clientX509CertUrl + privateKeyId = credentials.privateKeyId + projectId = credentials.projectId + tokenUri = credentials.tokenUri + type = credentials.type + universeDomain = credentials.universeDomain + additionalProperties = credentials.additionalProperties.toMutableMap() + } + + fun clientEmail(clientEmail: String) = clientEmail(JsonField.of(clientEmail)) + + /** + * Sets [Builder.clientEmail] to an arbitrary JSON value. + * + * You should usually call [Builder.clientEmail] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun clientEmail(clientEmail: JsonField) = apply { + this.clientEmail = clientEmail + } + + fun privateKey(privateKey: String) = privateKey(JsonField.of(privateKey)) + + /** + * Sets [Builder.privateKey] to an arbitrary JSON value. + * + * You should usually call [Builder.privateKey] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun privateKey(privateKey: JsonField) = apply { + this.privateKey = privateKey + } + + fun authProviderX509CertUrl(authProviderX509CertUrl: String) = + authProviderX509CertUrl(JsonField.of(authProviderX509CertUrl)) + + /** + * Sets [Builder.authProviderX509CertUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.authProviderX509CertUrl] with a well-typed + * [String] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun authProviderX509CertUrl(authProviderX509CertUrl: JsonField) = + apply { + this.authProviderX509CertUrl = authProviderX509CertUrl + } + + fun authUri(authUri: String) = authUri(JsonField.of(authUri)) + + /** + * Sets [Builder.authUri] to an arbitrary JSON value. + * + * You should usually call [Builder.authUri] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun authUri(authUri: JsonField) = apply { this.authUri = authUri } + + fun clientId(clientId: String) = clientId(JsonField.of(clientId)) + + /** + * Sets [Builder.clientId] to an arbitrary JSON value. + * + * You should usually call [Builder.clientId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun clientId(clientId: JsonField) = apply { this.clientId = clientId } + + fun clientX509CertUrl(clientX509CertUrl: String) = + clientX509CertUrl(JsonField.of(clientX509CertUrl)) + + /** + * Sets [Builder.clientX509CertUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.clientX509CertUrl] with a well-typed + * [String] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun clientX509CertUrl(clientX509CertUrl: JsonField) = apply { + this.clientX509CertUrl = clientX509CertUrl + } + + fun privateKeyId(privateKeyId: String) = + privateKeyId(JsonField.of(privateKeyId)) + + /** + * Sets [Builder.privateKeyId] to an arbitrary JSON value. + * + * You should usually call [Builder.privateKeyId] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun privateKeyId(privateKeyId: JsonField) = apply { + this.privateKeyId = privateKeyId + } + + fun projectId(projectId: String) = projectId(JsonField.of(projectId)) + + /** + * Sets [Builder.projectId] to an arbitrary JSON value. + * + * You should usually call [Builder.projectId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun projectId(projectId: JsonField) = apply { + this.projectId = projectId + } + + fun tokenUri(tokenUri: String) = tokenUri(JsonField.of(tokenUri)) + + /** + * Sets [Builder.tokenUri] to an arbitrary JSON value. + * + * You should usually call [Builder.tokenUri] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun tokenUri(tokenUri: JsonField) = apply { this.tokenUri = tokenUri } + + 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 universeDomain(universeDomain: String) = + universeDomain(JsonField.of(universeDomain)) + + /** + * Sets [Builder.universeDomain] to an arbitrary JSON value. + * + * You should usually call [Builder.universeDomain] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun universeDomain(universeDomain: JsonField) = apply { + this.universeDomain = universeDomain + } + + 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 [Credentials]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .clientEmail() + * .privateKey() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Credentials = + Credentials( + checkRequired("clientEmail", clientEmail), + checkRequired("privateKey", privateKey), + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Credentials = apply { + if (validated) { + return@apply + } + + clientEmail() + privateKey() + authProviderX509CertUrl() + authUri() + clientId() + clientX509CertUrl() + privateKeyId() + projectId() + tokenUri() + type().ifPresent { it.validate() } + universeDomain() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (clientEmail.asKnown().isPresent) 1 else 0) + + (if (privateKey.asKnown().isPresent) 1 else 0) + + (if (authProviderX509CertUrl.asKnown().isPresent) 1 else 0) + + (if (authUri.asKnown().isPresent) 1 else 0) + + (if (clientId.asKnown().isPresent) 1 else 0) + + (if (clientX509CertUrl.asKnown().isPresent) 1 else 0) + + (if (privateKeyId.asKnown().isPresent) 1 else 0) + + (if (projectId.asKnown().isPresent) 1 else 0) + + (if (tokenUri.asKnown().isPresent) 1 else 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + (if (universeDomain.asKnown().isPresent) 1 else 0) + + 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 SERVICE_ACCOUNT = of("service_account") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + SERVICE_ACCOUNT + } + + /** + * 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 { + SERVICE_ACCOUNT, + /** + * 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) { + SERVICE_ACCOUNT -> Value.SERVICE_ACCOUNT + 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 StagehandInvalidDataException if this class instance's value is a not + * a known member. + */ + fun known(): Known = + when (this) { + SERVICE_ACCOUNT -> Known.SERVICE_ACCOUNT + else -> throw StagehandInvalidDataException("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 StagehandInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + StagehandInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Type && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Credentials && + clientEmail == other.clientEmail && + privateKey == other.privateKey && + authProviderX509CertUrl == other.authProviderX509CertUrl && + authUri == other.authUri && + clientId == other.clientId && + clientX509CertUrl == other.clientX509CertUrl && + privateKeyId == other.privateKeyId && + projectId == other.projectId && + tokenUri == other.tokenUri && + type == other.type && + universeDomain == other.universeDomain && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + clientEmail, + privateKey, + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Credentials{clientEmail=$clientEmail, privateKey=$privateKey, authProviderX509CertUrl=$authProviderX509CertUrl, authUri=$authUri, clientId=$clientId, clientX509CertUrl=$clientX509CertUrl, privateKeyId=$privateKeyId, projectId=$projectId, tokenUri=$tokenUri, type=$type, universeDomain=$universeDomain, additionalProperties=$additionalProperties}" + } + + /** Google auth scopes for the desired API request */ + @JsonDeserialize(using = Scopes.Deserializer::class) + @JsonSerialize(using = Scopes.Serializer::class) + class Scopes + private constructor( + private val string: String? = null, + private val strings: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun strings(): Optional> = Optional.ofNullable(strings) + + fun isString(): Boolean = string != null + + fun isStrings(): Boolean = strings != null + + fun asString(): String = string.getOrThrow("string") + + fun asStrings(): List = strings.getOrThrow("strings") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the + * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not known + * to this version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.browserbase.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = scopes.accept(new Scopes.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws StagehandInvalidDataException if [Visitor.unknown] is not overridden in + * [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + strings != null -> visitor.visitStrings(strings) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Scopes = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitStrings(strings: List) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 visitStrings(strings: List) = strings.size + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Scopes && string == other.string && strings == other.strings + } + + override fun hashCode(): Int = Objects.hash(string, strings) + + override fun toString(): String = + when { + string != null -> "Scopes{string=$string}" + strings != null -> "Scopes{strings=$strings}" + _json != null -> "Scopes{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Scopes") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Scopes(string = string) + + @JvmStatic + fun ofStrings(strings: List) = Scopes(strings = strings.toImmutable()) + } + + /** + * An interface that defines how to map each variant of [Scopes] to a value of type + * [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitStrings(strings: List): T + + /** + * Maps an unknown variant of [Scopes] to a value of type [T]. + * + * An instance of [Scopes] 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 StagehandInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw StagehandInvalidDataException("Unknown Scopes: $json") + } + } + + internal class Deserializer : BaseDeserializer(Scopes::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Scopes { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Scopes(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>())?.let { + Scopes(strings = 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 -> Scopes(_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(Scopes::class) { + + override fun serialize( + value: Scopes, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.strings != null -> generator.writeObject(value.strings) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Scopes") + } + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Auth && + credentials == other.credentials && + type == other.type && + projectId == other.projectId && + scopes == other.scopes && + universeDomain == other.universeDomain && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + credentials, + type, + projectId, + scopes, + universeDomain, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Auth{credentials=$credentials, type=$type, projectId=$projectId, scopes=$scopes, universeDomain=$universeDomain, additionalProperties=$additionalProperties}" + } + + /** Vertex provider-specific model configuration */ + class ProviderOptions + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val vertex: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("vertex") @ExcludeMissing vertex: JsonField = JsonMissing.of() + ) : this(vertex, mutableMapOf()) + + /** + * Vertex AI provider-specific settings + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun vertex(): Vertex = vertex.getRequired("vertex") + + /** + * Returns the raw JSON value of [vertex]. + * + * Unlike [vertex], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("vertex") @ExcludeMissing fun _vertex(): JsonField = vertex + + @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 [ProviderOptions]. + * + * The following fields are required: + * ```java + * .vertex() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ProviderOptions]. */ + class Builder internal constructor() { + + private var vertex: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(providerOptions: ProviderOptions) = apply { + vertex = providerOptions.vertex + additionalProperties = providerOptions.additionalProperties.toMutableMap() + } + + /** Vertex AI provider-specific settings */ + fun vertex(vertex: Vertex) = vertex(JsonField.of(vertex)) + + /** + * Sets [Builder.vertex] to an arbitrary JSON value. + * + * You should usually call [Builder.vertex] with a well-typed [Vertex] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun vertex(vertex: JsonField) = apply { this.vertex = vertex } + + 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 [ProviderOptions]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .vertex() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ProviderOptions = + ProviderOptions( + checkRequired("vertex", vertex), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): ProviderOptions = apply { + if (validated) { + return@apply + } + + vertex().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = (vertex.asKnown().getOrNull()?.validity() ?: 0) + + /** Vertex AI provider-specific settings */ + class Vertex + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val location: JsonField, + private val project: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("location") + @ExcludeMissing + location: JsonField = JsonMissing.of(), + @JsonProperty("project") + @ExcludeMissing + project: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + ) : this(location, project, baseUrl, headers, mutableMapOf()) + + /** + * Google Cloud location for Vertex AI models + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun location(): String = location.getRequired("location") + + /** + * Google Cloud project ID for Vertex AI models + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun project(): String = project.getRequired("project") + + /** + * Base URL for the Vertex AI provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the Vertex AI provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * Returns the raw JSON value of [location]. + * + * Unlike [location], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("location") + @ExcludeMissing + fun _location(): JsonField = location + + /** + * Returns the raw JSON value of [project]. + * + * Unlike [project], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("project") @ExcludeMissing fun _project(): JsonField = project + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + @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 [Vertex]. + * + * The following fields are required: + * ```java + * .location() + * .project() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Vertex]. */ + class Builder internal constructor() { + + private var location: JsonField? = null + private var project: JsonField? = null + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(vertex: Vertex) = apply { + location = vertex.location + project = vertex.project + baseUrl = vertex.baseUrl + headers = vertex.headers + additionalProperties = vertex.additionalProperties.toMutableMap() + } + + /** Google Cloud location for Vertex AI models */ + fun location(location: String) = location(JsonField.of(location)) + + /** + * Sets [Builder.location] to an arbitrary JSON value. + * + * You should usually call [Builder.location] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun location(location: JsonField) = apply { this.location = location } + + /** Google Cloud project ID for Vertex AI models */ + fun project(project: String) = project(JsonField.of(project)) + + /** + * Sets [Builder.project] to an arbitrary JSON value. + * + * You should usually call [Builder.project] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun project(project: JsonField) = apply { this.project = project } + + /** Base URL for the Vertex AI provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl } + + /** Custom headers sent with every request to the Vertex AI provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun headers(headers: JsonField) = apply { this.headers = headers } + + 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 [Vertex]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .location() + * .project() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Vertex = + Vertex( + checkRequired("location", location), + checkRequired("project", project), + baseUrl, + headers, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Vertex = apply { + if (validated) { + return@apply + } + + location() + project() + baseUrl() + headers().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (location.asKnown().isPresent) 1 else 0) + + (if (project.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + /** Custom headers sent with every request to the Vertex AI provider */ + class Headers + @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 [Headers]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Headers{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Vertex && + location == other.location && + project == other.project && + baseUrl == other.baseUrl && + headers == other.headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(location, project, baseUrl, headers, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Vertex{location=$location, project=$project, baseUrl=$baseUrl, headers=$headers, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ProviderOptions && + vertex == other.vertex && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(vertex, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ProviderOptions{vertex=$vertex, additionalProperties=$additionalProperties}" + } + + /** Custom headers sent with every request to the model provider */ + class Headers + @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 [Headers]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 + } - /** - * Returns the raw JSON value of [headers]. - * - * Unlike [headers], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("headers") @ExcludeMissing fun _headers(): JsonField = headers + return other is Headers && additionalProperties == other.additionalProperties + } - /** - * Returns the raw JSON value of [provider]. - * - * Unlike [provider], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonField = provider + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } + override fun hashCode(): Int = hashCode - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) + override fun toString() = "Headers{additionalProperties=$additionalProperties}" + } - fun toBuilder() = Builder().from(this) + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - companion object { + return other is VertexModelConfigObject && + auth == other.auth && + modelName == other.modelName && + provider == other.provider && + providerOptions == other.providerOptions && + apiKey == other.apiKey && + baseUrl == other.baseUrl && + headers == other.headers && + additionalProperties == other.additionalProperties + } - /** - * Returns a mutable builder for constructing an instance of [ModelConfig]. - * - * The following fields are required: - * ```java - * .modelName() - * ``` - */ - @JvmStatic fun builder() = Builder() - } + private val hashCode: Int by lazy { + Objects.hash( + auth, + modelName, + provider, + providerOptions, + apiKey, + baseUrl, + headers, + additionalProperties, + ) + } - /** A builder for [ModelConfig]. */ - class Builder internal constructor() { + override fun hashCode(): Int = hashCode - private var modelName: JsonField? = null - private var apiKey: JsonField = JsonMissing.of() - private var baseUrl: JsonField = JsonMissing.of() - private var headers: JsonField = JsonMissing.of() - private var provider: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() + override fun toString() = + "VertexModelConfigObject{auth=$auth, modelName=$modelName, provider=$provider, providerOptions=$providerOptions, apiKey=$apiKey, baseUrl=$baseUrl, headers=$headers, additionalProperties=$additionalProperties}" + } - @JvmSynthetic - internal fun from(modelConfig: ModelConfig) = apply { - modelName = modelConfig.modelName - apiKey = modelConfig.apiKey - baseUrl = modelConfig.baseUrl - headers = modelConfig.headers - provider = modelConfig.provider - additionalProperties = modelConfig.additionalProperties.toMutableMap() - } + class GenericModelConfigObject + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val modelName: JsonField, + private val apiKey: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val provider: JsonField, + private val additionalProperties: MutableMap, + ) { - /** Model name string with provider prefix (e.g., 'openai/gpt-5-nano') */ - fun modelName(modelName: String) = modelName(JsonField.of(modelName)) + @JsonCreator + private constructor( + @JsonProperty("modelName") + @ExcludeMissing + modelName: JsonField = JsonMissing.of(), + @JsonProperty("apiKey") @ExcludeMissing apiKey: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") @ExcludeMissing baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") @ExcludeMissing headers: JsonField = JsonMissing.of(), + @JsonProperty("provider") + @ExcludeMissing + provider: JsonField = JsonMissing.of(), + ) : this(modelName, apiKey, baseUrl, headers, provider, mutableMapOf()) /** - * Sets [Builder.modelName] to an arbitrary JSON value. + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano') * - * You should usually call [Builder.modelName] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ - fun modelName(modelName: JsonField) = apply { this.modelName = modelName } - - /** API key for the model provider */ - fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + fun modelName(): String = modelName.getRequired("modelName") /** - * Sets [Builder.apiKey] to an arbitrary JSON value. + * API key for the model provider * - * You should usually call [Builder.apiKey] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } - - /** Base URL for the model provider */ - fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + fun apiKey(): Optional = apiKey.getOptional("apiKey") /** - * Sets [Builder.baseUrl] to an arbitrary JSON value. + * Base URL for the model provider * - * You should usually call [Builder.baseUrl] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl } - - /** Custom headers sent with every request to the model provider */ - fun headers(headers: Headers) = headers(JsonField.of(headers)) + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") /** - * Sets [Builder.headers] to an arbitrary JSON value. + * Custom headers sent with every request to the model provider * - * You should usually call [Builder.headers] with a well-typed [Headers] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun headers(headers: JsonField) = apply { this.headers = headers } - - /** AI provider for the model (or provide a baseURL endpoint instead) */ - fun provider(provider: Provider) = provider(JsonField.of(provider)) + fun headers(): Optional = headers.getOptional("headers") /** - * Sets [Builder.provider] to an arbitrary JSON value. + * AI provider for the model (or provide a baseURL endpoint instead) * - * You should usually call [Builder.provider] with a well-typed [Provider] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun provider(provider: JsonField) = apply { this.provider = provider } - - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) - } - - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) - } - - fun putAllAdditionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.putAll(additionalProperties) - } - - fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } - - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + fun provider(): Optional = provider.getOptional("provider") /** - * Returns an immutable instance of [ModelConfig]. - * - * Further updates to this [Builder] will not mutate the returned instance. + * Returns the raw JSON value of [modelName]. * - * The following fields are required: - * ```java - * .modelName() - * ``` + * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("modelName") @ExcludeMissing fun _modelName(): JsonField = modelName + + /** + * Returns the raw JSON value of [apiKey]. * - * @throws IllegalStateException if any required field is unset. + * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected type. */ - fun build(): ModelConfig = - ModelConfig( - checkRequired("modelName", modelName), - apiKey, - baseUrl, - headers, - provider, - additionalProperties.toMutableMap(), - ) - } + @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey - private var validated: Boolean = false + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl - /** - * Validates that the types of all values in this object match their expected types recursively. - * - * This method is _not_ forwards compatible with new types from the API for existing fields. - * - * @throws StagehandInvalidDataException if any value type in this object doesn't match its - * expected type. - */ - fun validate(): ModelConfig = apply { - if (validated) { - return@apply - } + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("headers") @ExcludeMissing fun _headers(): JsonField = headers - modelName() - apiKey() - baseUrl() - headers().ifPresent { it.validate() } - provider().ifPresent { it.validate() } - validated = true - } + /** + * Returns the raw JSON value of [provider]. + * + * Unlike [provider], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonField = provider - fun isValid(): Boolean = - try { - validate() - true - } catch (e: StagehandInvalidDataException) { - false + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) } - /** - * 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 (modelName.asKnown().isPresent) 1 else 0) + - (if (apiKey.asKnown().isPresent) 1 else 0) + - (if (baseUrl.asKnown().isPresent) 1 else 0) + - (headers.asKnown().getOrNull()?.validity() ?: 0) + - (provider.asKnown().getOrNull()?.validity() ?: 0) - - /** Custom headers sent with every request to the model provider */ - class Headers - @JsonCreator - private constructor( - @com.fasterxml.jackson.annotation.JsonValue - private val additionalProperties: Map - ) { - @JsonAnyGetter @ExcludeMissing - fun _additionalProperties(): Map = additionalProperties + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) fun toBuilder() = Builder().from(this) companion object { - /** Returns a mutable builder for constructing an instance of [Headers]. */ + /** + * Returns a mutable builder for constructing an instance of [GenericModelConfigObject]. + * + * The following fields are required: + * ```java + * .modelName() + * ``` + */ @JvmStatic fun builder() = Builder() } - /** A builder for [Headers]. */ + /** A builder for [GenericModelConfigObject]. */ class Builder internal constructor() { + private var modelName: JsonField? = null + private var apiKey: JsonField = JsonMissing.of() + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var provider: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(headers: Headers) = apply { - additionalProperties = headers.additionalProperties.toMutableMap() + internal fun from(genericModelConfigObject: GenericModelConfigObject) = apply { + modelName = genericModelConfigObject.modelName + apiKey = genericModelConfigObject.apiKey + baseUrl = genericModelConfigObject.baseUrl + headers = genericModelConfigObject.headers + provider = genericModelConfigObject.provider + additionalProperties = genericModelConfigObject.additionalProperties.toMutableMap() } + /** Model name string with provider prefix (e.g., 'openai/gpt-5-nano') */ + fun modelName(modelName: String) = modelName(JsonField.of(modelName)) + + /** + * Sets [Builder.modelName] to an arbitrary JSON value. + * + * You should usually call [Builder.modelName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun modelName(modelName: JsonField) = apply { this.modelName = modelName } + + /** API key for the model provider */ + fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + + /** + * Sets [Builder.apiKey] to an arbitrary JSON value. + * + * You should usually call [Builder.apiKey] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } + + /** Base URL for the model provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl } + + /** Custom headers sent with every request to the model provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun headers(headers: JsonField) = apply { this.headers = headers } + + /** AI provider for the model (or provide a baseURL endpoint instead) */ + fun provider(provider: Provider) = provider(JsonField.of(provider)) + + /** + * Sets [Builder.provider] to an arbitrary JSON value. + * + * You should usually call [Builder.provider] with a well-typed [Provider] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun provider(provider: JsonField) = apply { this.provider = provider } + fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() putAllAdditionalProperties(additionalProperties) @@ -352,11 +2913,26 @@ private constructor( } /** - * Returns an immutable instance of [Headers]. + * Returns an immutable instance of [GenericModelConfigObject]. * * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .modelName() + * ``` + * + * @throws IllegalStateException if any required field is unset. */ - fun build(): Headers = Headers(additionalProperties.toImmutable()) + fun build(): GenericModelConfigObject = + GenericModelConfigObject( + checkRequired("modelName", modelName), + apiKey, + baseUrl, + headers, + provider, + additionalProperties.toMutableMap(), + ) } private var validated: Boolean = false @@ -370,11 +2946,16 @@ private constructor( * @throws StagehandInvalidDataException if any value type in this object doesn't match its * expected type. */ - fun validate(): Headers = apply { + fun validate(): GenericModelConfigObject = apply { if (validated) { return@apply } + modelName() + apiKey() + baseUrl() + headers().ifPresent { it.validate() } + provider().ifPresent { it.validate() } validated = true } @@ -394,198 +2975,305 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + (if (modelName.asKnown().isPresent) 1 else 0) + + (if (apiKey.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + (provider.asKnown().getOrNull()?.validity() ?: 0) - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + /** Custom headers sent with every request to the model provider */ + class Headers + @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 [Headers]. */ + @JvmStatic fun builder() = Builder() } - return other is Headers && additionalProperties == other.additionalProperties - } + /** A builder for [Headers]. */ + class Builder internal constructor() { - private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + private var additionalProperties: MutableMap = mutableMapOf() - override fun hashCode(): Int = hashCode + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.additionalProperties.toMutableMap() + } - override fun toString() = "Headers{additionalProperties=$additionalProperties}" - } + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - /** AI provider for the model (or provide a baseURL endpoint instead) */ - class Provider @JsonCreator private constructor(private val value: JsonField) : Enum { + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - /** - * 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 + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } - companion object { + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } - @JvmField val OPENAI = of("openai") + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } - @JvmField val ANTHROPIC = of("anthropic") + /** + * Returns an immutable instance of [Headers]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } - @JvmField val GOOGLE = of("google") + private var validated: Boolean = false - @JvmField val MICROSOFT = of("microsoft") + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } - @JvmField val BEDROCK = of("bedrock") + validated = true + } - @JvmStatic fun of(value: String) = Provider(JsonField.of(value)) - } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + false + } - /** An enum containing [Provider]'s known values. */ - enum class Known { - OPENAI, - ANTHROPIC, - GOOGLE, - MICROSOFT, - BEDROCK, - } + /** + * 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() } - /** - * An enum containing [Provider]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Provider] 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 { - OPENAI, - ANTHROPIC, - GOOGLE, - MICROSOFT, - BEDROCK, - /** An enum member indicating that [Provider] was instantiated with an unknown value. */ - _UNKNOWN, + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Headers && additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Headers{additionalProperties=$additionalProperties}" } - /** - * 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) { - OPENAI -> Value.OPENAI - ANTHROPIC -> Value.ANTHROPIC - GOOGLE -> Value.GOOGLE - MICROSOFT -> Value.MICROSOFT - BEDROCK -> Value.BEDROCK - else -> Value._UNKNOWN + /** AI provider for the model (or provide a baseURL endpoint instead) */ + class Provider @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 OPENAI = of("openai") + + @JvmField val ANTHROPIC = of("anthropic") + + @JvmField val GOOGLE = of("google") + + @JvmField val MICROSOFT = of("microsoft") + + @JvmField val BEDROCK = of("bedrock") + + @JvmStatic fun of(value: String) = Provider(JsonField.of(value)) } - /** - * 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 StagehandInvalidDataException if this class instance's value is a not a known - * member. - */ - fun known(): Known = - when (this) { - OPENAI -> Known.OPENAI - ANTHROPIC -> Known.ANTHROPIC - GOOGLE -> Known.GOOGLE - MICROSOFT -> Known.MICROSOFT - BEDROCK -> Known.BEDROCK - else -> throw StagehandInvalidDataException("Unknown Provider: $value") + /** An enum containing [Provider]'s known values. */ + enum class Known { + OPENAI, + ANTHROPIC, + GOOGLE, + MICROSOFT, + BEDROCK, } - /** - * 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 StagehandInvalidDataException if this class instance's value does not have the - * expected primitive type. - */ - fun asString(): String = - _value().asString().orElseThrow { - StagehandInvalidDataException("Value is not a String") + /** + * An enum containing [Provider]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Provider] 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 { + OPENAI, + ANTHROPIC, + GOOGLE, + MICROSOFT, + BEDROCK, + /** + * An enum member indicating that [Provider] was instantiated with an unknown value. + */ + _UNKNOWN, } - private var validated: Boolean = false + /** + * 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) { + OPENAI -> Value.OPENAI + ANTHROPIC -> Value.ANTHROPIC + GOOGLE -> Value.GOOGLE + MICROSOFT -> Value.MICROSOFT + BEDROCK -> Value.BEDROCK + else -> Value._UNKNOWN + } - /** - * Validates that the types of all values in this object match their expected types - * recursively. - * - * This method is _not_ forwards compatible with new types from the API for existing fields. - * - * @throws StagehandInvalidDataException if any value type in this object doesn't match its - * expected type. - */ - fun validate(): Provider = apply { - if (validated) { - return@apply + /** + * 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 StagehandInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + OPENAI -> Known.OPENAI + ANTHROPIC -> Known.ANTHROPIC + GOOGLE -> Known.GOOGLE + MICROSOFT -> Known.MICROSOFT + BEDROCK -> Known.BEDROCK + else -> throw StagehandInvalidDataException("Unknown Provider: $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 StagehandInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + StagehandInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Provider = apply { + if (validated) { + return@apply + } + + known() + validated = true } - known() - validated = true - } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + false + } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: StagehandInvalidDataException) { - 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 other is Provider && value == other.value } - /** - * 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 hashCode() = value.hashCode() + + override fun toString() = value.toString() + } override fun equals(other: Any?): Boolean { if (this === other) { return true } - return other is Provider && value == other.value + return other is GenericModelConfigObject && + modelName == other.modelName && + apiKey == other.apiKey && + baseUrl == other.baseUrl && + headers == other.headers && + provider == other.provider && + additionalProperties == other.additionalProperties } - override fun hashCode() = value.hashCode() - - override fun toString() = value.toString() - } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + private val hashCode: Int by lazy { + Objects.hash(modelName, apiKey, baseUrl, headers, provider, additionalProperties) } - return other is ModelConfig && - modelName == other.modelName && - apiKey == other.apiKey && - baseUrl == other.baseUrl && - headers == other.headers && - provider == other.provider && - additionalProperties == other.additionalProperties - } + override fun hashCode(): Int = hashCode - private val hashCode: Int by lazy { - Objects.hash(modelName, apiKey, baseUrl, headers, provider, additionalProperties) + override fun toString() = + "GenericModelConfigObject{modelName=$modelName, apiKey=$apiKey, baseUrl=$baseUrl, headers=$headers, provider=$provider, additionalProperties=$additionalProperties}" } - - override fun hashCode(): Int = hashCode - - override fun toString() = - "ModelConfig{modelName=$modelName, apiKey=$apiKey, baseUrl=$baseUrl, headers=$headers, provider=$provider, additionalProperties=$additionalProperties}" } diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActParams.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActParams.kt index a72cbeb..3085f28 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActParams.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActParams.kt @@ -929,8 +929,19 @@ private constructor( */ fun model(model: JsonField) = apply { this.model = model } - /** Alias for calling [model] with `Model.ofConfig(config)`. */ - fun model(config: ModelConfig) = model(Model.ofConfig(config)) + /** + * Alias for calling [model] with + * `Model.ofVertexModelConfigObject(vertexModelConfigObject)`. + */ + fun model(vertexModelConfigObject: Model.VertexModelConfigObject) = + model(Model.ofVertexModelConfigObject(vertexModelConfigObject)) + + /** + * Alias for calling [model] with + * `Model.ofGenericModelConfigObject(genericModelConfigObject)`. + */ + fun model(genericModelConfigObject: Model.GenericModelConfigObject) = + model(Model.ofGenericModelConfigObject(genericModelConfigObject)) /** Alias for calling [model] with `Model.ofString(string)`. */ fun model(string: String) = model(Model.ofString(string)) @@ -1037,20 +1048,31 @@ private constructor( @JsonSerialize(using = Model.Serializer::class) class Model private constructor( - private val config: ModelConfig? = null, + private val vertexModelConfigObject: VertexModelConfigObject? = null, + private val genericModelConfigObject: GenericModelConfigObject? = null, private val string: String? = null, private val _json: JsonValue? = null, ) { - fun config(): Optional = Optional.ofNullable(config) + fun vertexModelConfigObject(): Optional = + Optional.ofNullable(vertexModelConfigObject) + + fun genericModelConfigObject(): Optional = + Optional.ofNullable(genericModelConfigObject) fun string(): Optional = Optional.ofNullable(string) - fun isConfig(): Boolean = config != null + fun isVertexModelConfigObject(): Boolean = vertexModelConfigObject != null + + fun isGenericModelConfigObject(): Boolean = genericModelConfigObject != null fun isString(): Boolean = string != null - fun asConfig(): ModelConfig = config.getOrThrow("config") + fun asVertexModelConfigObject(): VertexModelConfigObject = + vertexModelConfigObject.getOrThrow("vertexModelConfigObject") + + fun asGenericModelConfigObject(): GenericModelConfigObject = + genericModelConfigObject.getOrThrow("genericModelConfigObject") fun asString(): String = string.getOrThrow("string") @@ -1069,8 +1091,8 @@ private constructor( * * Optional result = model.accept(new Model.Visitor>() { * @Override - * public Optional visitConfig(ModelConfig config) { - * return Optional.of(config.toString()); + * public Optional visitVertexModelConfigObject(VertexModelConfigObject vertexModelConfigObject) { + * return Optional.of(vertexModelConfigObject.toString()); * } * * // ... @@ -1088,7 +1110,10 @@ private constructor( */ fun accept(visitor: Visitor): T = when { - config != null -> visitor.visitConfig(config) + vertexModelConfigObject != null -> + visitor.visitVertexModelConfigObject(vertexModelConfigObject) + genericModelConfigObject != null -> + visitor.visitGenericModelConfigObject(genericModelConfigObject) string != null -> visitor.visitString(string) else -> visitor.unknown(_json) } @@ -1112,8 +1137,16 @@ private constructor( accept( object : Visitor { - override fun visitConfig(config: ModelConfig) { - config.validate() + override fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ) { + vertexModelConfigObject.validate() + } + + override fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ) { + genericModelConfigObject.validate() } override fun visitString(string: String) {} @@ -1140,7 +1173,13 @@ private constructor( internal fun validity(): Int = accept( object : Visitor { - override fun visitConfig(config: ModelConfig) = config.validity() + override fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ) = vertexModelConfigObject.validity() + + override fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ) = genericModelConfigObject.validity() override fun visitString(string: String) = 1 @@ -1153,14 +1192,21 @@ private constructor( return true } - return other is Model && config == other.config && string == other.string + return other is Model && + vertexModelConfigObject == other.vertexModelConfigObject && + genericModelConfigObject == other.genericModelConfigObject && + string == other.string } - override fun hashCode(): Int = Objects.hash(config, string) + override fun hashCode(): Int = + Objects.hash(vertexModelConfigObject, genericModelConfigObject, string) override fun toString(): String = when { - config != null -> "Model{config=$config}" + vertexModelConfigObject != null -> + "Model{vertexModelConfigObject=$vertexModelConfigObject}" + genericModelConfigObject != null -> + "Model{genericModelConfigObject=$genericModelConfigObject}" string != null -> "Model{string=$string}" _json != null -> "Model{_unknown=$_json}" else -> throw IllegalStateException("Invalid Model") @@ -1168,7 +1214,13 @@ private constructor( companion object { - @JvmStatic fun ofConfig(config: ModelConfig) = Model(config = config) + @JvmStatic + fun ofVertexModelConfigObject(vertexModelConfigObject: VertexModelConfigObject) = + Model(vertexModelConfigObject = vertexModelConfigObject) + + @JvmStatic + fun ofGenericModelConfigObject(genericModelConfigObject: GenericModelConfigObject) = + Model(genericModelConfigObject = genericModelConfigObject) @JvmStatic fun ofString(string: String) = Model(string = string) } @@ -1178,7 +1230,13 @@ private constructor( */ interface Visitor { - fun visitConfig(config: ModelConfig): T + fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ): T + + fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ): T fun visitString(string: String): T @@ -1204,9 +1262,10 @@ private constructor( val bestMatches = sequenceOf( - tryDeserialize(node, jacksonTypeRef())?.let { - Model(config = it, _json = json) - }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { Model(vertexModelConfigObject = it, _json = json) }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { Model(genericModelConfigObject = it, _json = json) }, tryDeserialize(node, jacksonTypeRef())?.let { Model(string = it, _json = json) }, @@ -1235,13 +1294,3179 @@ private constructor( provider: SerializerProvider, ) { when { - value.config != null -> generator.writeObject(value.config) + value.vertexModelConfigObject != null -> + generator.writeObject(value.vertexModelConfigObject) + value.genericModelConfigObject != null -> + generator.writeObject(value.genericModelConfigObject) value.string != null -> generator.writeObject(value.string) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid Model") } } } + + class VertexModelConfigObject + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val auth: JsonField, + private val modelName: JsonField, + private val provider: JsonValue, + private val providerOptions: JsonField, + private val apiKey: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("auth") @ExcludeMissing auth: JsonField = JsonMissing.of(), + @JsonProperty("modelName") + @ExcludeMissing + modelName: JsonField = JsonMissing.of(), + @JsonProperty("provider") + @ExcludeMissing + provider: JsonValue = JsonMissing.of(), + @JsonProperty("providerOptions") + @ExcludeMissing + providerOptions: JsonField = JsonMissing.of(), + @JsonProperty("apiKey") + @ExcludeMissing + apiKey: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + ) : this( + auth, + modelName, + provider, + providerOptions, + apiKey, + baseUrl, + headers, + mutableMapOf(), + ) + + /** + * Vertex provider authentication configuration + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun auth(): Auth = auth.getRequired("auth") + + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun modelName(): String = modelName.getRequired("modelName") + + /** + * Vertex AI model provider + * + * Expected to always return the following: + * ```java + * JsonValue.from("vertex") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonValue = provider + + /** + * Vertex provider-specific model configuration + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun providerOptions(): ProviderOptions = + providerOptions.getRequired("providerOptions") + + /** + * API key for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun apiKey(): Optional = apiKey.getOptional("apiKey") + + /** + * Base URL for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * Returns the raw JSON value of [auth]. + * + * Unlike [auth], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("auth") @ExcludeMissing fun _auth(): JsonField = auth + + /** + * Returns the raw JSON value of [modelName]. + * + * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("modelName") + @ExcludeMissing + fun _modelName(): JsonField = modelName + + /** + * Returns the raw JSON value of [providerOptions]. + * + * Unlike [providerOptions], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("providerOptions") + @ExcludeMissing + fun _providerOptions(): JsonField = providerOptions + + /** + * Returns the raw JSON value of [apiKey]. + * + * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + @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 + * [VertexModelConfigObject]. + * + * The following fields are required: + * ```java + * .auth() + * .modelName() + * .providerOptions() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [VertexModelConfigObject]. */ + class Builder internal constructor() { + + private var auth: JsonField? = null + private var modelName: JsonField? = null + private var provider: JsonValue = JsonValue.from("vertex") + private var providerOptions: JsonField? = null + private var apiKey: JsonField = JsonMissing.of() + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(vertexModelConfigObject: VertexModelConfigObject) = apply { + auth = vertexModelConfigObject.auth + modelName = vertexModelConfigObject.modelName + provider = vertexModelConfigObject.provider + providerOptions = vertexModelConfigObject.providerOptions + apiKey = vertexModelConfigObject.apiKey + baseUrl = vertexModelConfigObject.baseUrl + headers = vertexModelConfigObject.headers + additionalProperties = + vertexModelConfigObject.additionalProperties.toMutableMap() + } + + /** Vertex provider authentication configuration */ + fun auth(auth: Auth) = auth(JsonField.of(auth)) + + /** + * Sets [Builder.auth] to an arbitrary JSON value. + * + * You should usually call [Builder.auth] with a well-typed [Auth] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun auth(auth: JsonField) = apply { this.auth = auth } + + /** Model name string with provider prefix (e.g., 'openai/gpt-5-nano') */ + fun modelName(modelName: String) = modelName(JsonField.of(modelName)) + + /** + * Sets [Builder.modelName] to an arbitrary JSON value. + * + * You should usually call [Builder.modelName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun modelName(modelName: JsonField) = apply { + this.modelName = modelName + } + + /** + * 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("vertex") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun provider(provider: JsonValue) = apply { this.provider = provider } + + /** Vertex provider-specific model configuration */ + fun providerOptions(providerOptions: ProviderOptions) = + providerOptions(JsonField.of(providerOptions)) + + /** + * Sets [Builder.providerOptions] to an arbitrary JSON value. + * + * You should usually call [Builder.providerOptions] with a well-typed + * [ProviderOptions] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun providerOptions(providerOptions: JsonField) = apply { + this.providerOptions = providerOptions + } + + /** API key for the model provider */ + fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + + /** + * Sets [Builder.apiKey] to an arbitrary JSON value. + * + * You should usually call [Builder.apiKey] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } + + /** Base URL for the model provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl } + + /** Custom headers sent with every request to the model provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun headers(headers: JsonField) = apply { this.headers = headers } + + 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 [VertexModelConfigObject]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .auth() + * .modelName() + * .providerOptions() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): VertexModelConfigObject = + VertexModelConfigObject( + checkRequired("auth", auth), + checkRequired("modelName", modelName), + provider, + checkRequired("providerOptions", providerOptions), + apiKey, + baseUrl, + headers, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): VertexModelConfigObject = apply { + if (validated) { + return@apply + } + + auth().validate() + modelName() + _provider().let { + if (it != JsonValue.from("vertex")) { + throw StagehandInvalidDataException( + "'provider' is invalid, received $it" + ) + } + } + providerOptions().validate() + apiKey() + baseUrl() + headers().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = + (auth.asKnown().getOrNull()?.validity() ?: 0) + + (if (modelName.asKnown().isPresent) 1 else 0) + + provider.let { if (it == JsonValue.from("vertex")) 1 else 0 } + + (providerOptions.asKnown().getOrNull()?.validity() ?: 0) + + (if (apiKey.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + /** Vertex provider authentication configuration */ + class Auth + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val credentials: JsonField, + private val type: JsonValue, + private val projectId: JsonField, + private val scopes: JsonField, + private val universeDomain: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("credentials") + @ExcludeMissing + credentials: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("projectId") + @ExcludeMissing + projectId: JsonField = JsonMissing.of(), + @JsonProperty("scopes") + @ExcludeMissing + scopes: JsonField = JsonMissing.of(), + @JsonProperty("universeDomain") + @ExcludeMissing + universeDomain: JsonField = JsonMissing.of(), + ) : this(credentials, type, projectId, scopes, universeDomain, mutableMapOf()) + + /** + * Google Cloud service account credentials + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with + * an unexpected value). + */ + fun credentials(): Credentials = credentials.getRequired("credentials") + + /** + * Use inline Google Cloud service account credentials for provider + * authentication + * + * Expected to always return the following: + * ```java + * JsonValue.from("googleServiceAccount") + * ``` + * + * 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 + + /** + * Google Cloud project ID used by google-auth-library + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun projectId(): Optional = projectId.getOptional("projectId") + + /** + * Google auth scopes for the desired API request + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun scopes(): Optional = scopes.getOptional("scopes") + + /** + * Google Cloud universe domain + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun universeDomain(): Optional = + universeDomain.getOptional("universeDomain") + + /** + * Returns the raw JSON value of [credentials]. + * + * Unlike [credentials], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("credentials") + @ExcludeMissing + fun _credentials(): JsonField = credentials + + /** + * Returns the raw JSON value of [projectId]. + * + * Unlike [projectId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("projectId") + @ExcludeMissing + fun _projectId(): JsonField = projectId + + /** + * Returns the raw JSON value of [scopes]. + * + * Unlike [scopes], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("scopes") + @ExcludeMissing + fun _scopes(): JsonField = scopes + + /** + * Returns the raw JSON value of [universeDomain]. + * + * Unlike [universeDomain], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("universeDomain") + @ExcludeMissing + fun _universeDomain(): JsonField = universeDomain + + @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 [Auth]. + * + * The following fields are required: + * ```java + * .credentials() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Auth]. */ + class Builder internal constructor() { + + private var credentials: JsonField? = null + private var type: JsonValue = JsonValue.from("googleServiceAccount") + private var projectId: JsonField = JsonMissing.of() + private var scopes: JsonField = JsonMissing.of() + private var universeDomain: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(auth: Auth) = apply { + credentials = auth.credentials + type = auth.type + projectId = auth.projectId + scopes = auth.scopes + universeDomain = auth.universeDomain + additionalProperties = auth.additionalProperties.toMutableMap() + } + + /** Google Cloud service account credentials */ + fun credentials(credentials: Credentials) = + credentials(JsonField.of(credentials)) + + /** + * Sets [Builder.credentials] to an arbitrary JSON value. + * + * You should usually call [Builder.credentials] with a well-typed + * [Credentials] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun credentials(credentials: JsonField) = apply { + this.credentials = credentials + } + + /** + * 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("googleServiceAccount") + * ``` + * + * This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** Google Cloud project ID used by google-auth-library */ + fun projectId(projectId: String) = projectId(JsonField.of(projectId)) + + /** + * Sets [Builder.projectId] to an arbitrary JSON value. + * + * You should usually call [Builder.projectId] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun projectId(projectId: JsonField) = apply { + this.projectId = projectId + } + + /** Google auth scopes for the desired API request */ + fun scopes(scopes: Scopes) = scopes(JsonField.of(scopes)) + + /** + * Sets [Builder.scopes] to an arbitrary JSON value. + * + * You should usually call [Builder.scopes] with a well-typed [Scopes] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun scopes(scopes: JsonField) = apply { this.scopes = scopes } + + /** Alias for calling [scopes] with `Scopes.ofString(string)`. */ + fun scopes(string: String) = scopes(Scopes.ofString(string)) + + /** Alias for calling [scopes] with `Scopes.ofStrings(strings)`. */ + fun scopesOfStrings(strings: List) = + scopes(Scopes.ofStrings(strings)) + + /** Google Cloud universe domain */ + fun universeDomain(universeDomain: String) = + universeDomain(JsonField.of(universeDomain)) + + /** + * Sets [Builder.universeDomain] to an arbitrary JSON value. + * + * You should usually call [Builder.universeDomain] with a well-typed + * [String] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun universeDomain(universeDomain: JsonField) = apply { + this.universeDomain = universeDomain + } + + 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 [Auth]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .credentials() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Auth = + Auth( + checkRequired("credentials", credentials), + type, + projectId, + scopes, + universeDomain, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Auth = apply { + if (validated) { + return@apply + } + + credentials().validate() + _type().let { + if (it != JsonValue.from("googleServiceAccount")) { + throw StagehandInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + projectId() + scopes().ifPresent { it.validate() } + universeDomain() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = + (credentials.asKnown().getOrNull()?.validity() ?: 0) + + type.let { + if (it == JsonValue.from("googleServiceAccount")) 1 else 0 + } + + (if (projectId.asKnown().isPresent) 1 else 0) + + (scopes.asKnown().getOrNull()?.validity() ?: 0) + + (if (universeDomain.asKnown().isPresent) 1 else 0) + + /** Google Cloud service account credentials */ + class Credentials + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val clientEmail: JsonField, + private val privateKey: JsonField, + private val authProviderX509CertUrl: JsonField, + private val authUri: JsonField, + private val clientId: JsonField, + private val clientX509CertUrl: JsonField, + private val privateKeyId: JsonField, + private val projectId: JsonField, + private val tokenUri: JsonField, + private val type: JsonField, + private val universeDomain: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("client_email") + @ExcludeMissing + clientEmail: JsonField = JsonMissing.of(), + @JsonProperty("private_key") + @ExcludeMissing + privateKey: JsonField = JsonMissing.of(), + @JsonProperty("auth_provider_x509_cert_url") + @ExcludeMissing + authProviderX509CertUrl: JsonField = JsonMissing.of(), + @JsonProperty("auth_uri") + @ExcludeMissing + authUri: JsonField = JsonMissing.of(), + @JsonProperty("client_id") + @ExcludeMissing + clientId: JsonField = JsonMissing.of(), + @JsonProperty("client_x509_cert_url") + @ExcludeMissing + clientX509CertUrl: JsonField = JsonMissing.of(), + @JsonProperty("private_key_id") + @ExcludeMissing + privateKeyId: JsonField = JsonMissing.of(), + @JsonProperty("project_id") + @ExcludeMissing + projectId: JsonField = JsonMissing.of(), + @JsonProperty("token_uri") + @ExcludeMissing + tokenUri: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonField = JsonMissing.of(), + @JsonProperty("universe_domain") + @ExcludeMissing + universeDomain: JsonField = JsonMissing.of(), + ) : this( + clientEmail, + privateKey, + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + mutableMapOf(), + ) + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun clientEmail(): String = clientEmail.getRequired("client_email") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun privateKey(): String = privateKey.getRequired("private_key") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun authProviderX509CertUrl(): Optional = + authProviderX509CertUrl.getOptional("auth_provider_x509_cert_url") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun authUri(): Optional = authUri.getOptional("auth_uri") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun clientId(): Optional = clientId.getOptional("client_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun clientX509CertUrl(): Optional = + clientX509CertUrl.getOptional("client_x509_cert_url") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun privateKeyId(): Optional = + privateKeyId.getOptional("private_key_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun projectId(): Optional = projectId.getOptional("project_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun tokenUri(): Optional = tokenUri.getOptional("token_uri") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun universeDomain(): Optional = + universeDomain.getOptional("universe_domain") + + /** + * Returns the raw JSON value of [clientEmail]. + * + * Unlike [clientEmail], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("client_email") + @ExcludeMissing + fun _clientEmail(): JsonField = clientEmail + + /** + * Returns the raw JSON value of [privateKey]. + * + * Unlike [privateKey], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("private_key") + @ExcludeMissing + fun _privateKey(): JsonField = privateKey + + /** + * Returns the raw JSON value of [authProviderX509CertUrl]. + * + * Unlike [authProviderX509CertUrl], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("auth_provider_x509_cert_url") + @ExcludeMissing + fun _authProviderX509CertUrl(): JsonField = authProviderX509CertUrl + + /** + * Returns the raw JSON value of [authUri]. + * + * Unlike [authUri], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("auth_uri") + @ExcludeMissing + fun _authUri(): JsonField = authUri + + /** + * Returns the raw JSON value of [clientId]. + * + * Unlike [clientId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("client_id") + @ExcludeMissing + fun _clientId(): JsonField = clientId + + /** + * Returns the raw JSON value of [clientX509CertUrl]. + * + * Unlike [clientX509CertUrl], this method doesn't throw if the JSON field + * has an unexpected type. + */ + @JsonProperty("client_x509_cert_url") + @ExcludeMissing + fun _clientX509CertUrl(): JsonField = clientX509CertUrl + + /** + * Returns the raw JSON value of [privateKeyId]. + * + * Unlike [privateKeyId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("private_key_id") + @ExcludeMissing + fun _privateKeyId(): JsonField = privateKeyId + + /** + * Returns the raw JSON value of [projectId]. + * + * Unlike [projectId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("project_id") + @ExcludeMissing + fun _projectId(): JsonField = projectId + + /** + * Returns the raw JSON value of [tokenUri]. + * + * Unlike [tokenUri], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("token_uri") + @ExcludeMissing + fun _tokenUri(): JsonField = tokenUri + + /** + * 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 [universeDomain]. + * + * Unlike [universeDomain], this method doesn't throw if the JSON field has + * an unexpected type. + */ + @JsonProperty("universe_domain") + @ExcludeMissing + fun _universeDomain(): JsonField = universeDomain + + @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 + * [Credentials]. + * + * The following fields are required: + * ```java + * .clientEmail() + * .privateKey() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Credentials]. */ + class Builder internal constructor() { + + private var clientEmail: JsonField? = null + private var privateKey: JsonField? = null + private var authProviderX509CertUrl: JsonField = + JsonMissing.of() + private var authUri: JsonField = JsonMissing.of() + private var clientId: JsonField = JsonMissing.of() + private var clientX509CertUrl: JsonField = JsonMissing.of() + private var privateKeyId: JsonField = JsonMissing.of() + private var projectId: JsonField = JsonMissing.of() + private var tokenUri: JsonField = JsonMissing.of() + private var type: JsonField = JsonMissing.of() + private var universeDomain: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(credentials: Credentials) = apply { + clientEmail = credentials.clientEmail + privateKey = credentials.privateKey + authProviderX509CertUrl = credentials.authProviderX509CertUrl + authUri = credentials.authUri + clientId = credentials.clientId + clientX509CertUrl = credentials.clientX509CertUrl + privateKeyId = credentials.privateKeyId + projectId = credentials.projectId + tokenUri = credentials.tokenUri + type = credentials.type + universeDomain = credentials.universeDomain + additionalProperties = + credentials.additionalProperties.toMutableMap() + } + + fun clientEmail(clientEmail: String) = + clientEmail(JsonField.of(clientEmail)) + + /** + * Sets [Builder.clientEmail] to an arbitrary JSON value. + * + * You should usually call [Builder.clientEmail] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun clientEmail(clientEmail: JsonField) = apply { + this.clientEmail = clientEmail + } + + fun privateKey(privateKey: String) = + privateKey(JsonField.of(privateKey)) + + /** + * Sets [Builder.privateKey] to an arbitrary JSON value. + * + * You should usually call [Builder.privateKey] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun privateKey(privateKey: JsonField) = apply { + this.privateKey = privateKey + } + + fun authProviderX509CertUrl(authProviderX509CertUrl: String) = + authProviderX509CertUrl(JsonField.of(authProviderX509CertUrl)) + + /** + * Sets [Builder.authProviderX509CertUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.authProviderX509CertUrl] with a + * well-typed [String] value instead. This method is primarily for + * setting the field to an undocumented or not yet supported value. + */ + fun authProviderX509CertUrl( + authProviderX509CertUrl: JsonField + ) = apply { this.authProviderX509CertUrl = authProviderX509CertUrl } + + fun authUri(authUri: String) = authUri(JsonField.of(authUri)) + + /** + * Sets [Builder.authUri] to an arbitrary JSON value. + * + * You should usually call [Builder.authUri] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun authUri(authUri: JsonField) = apply { + this.authUri = authUri + } + + fun clientId(clientId: String) = clientId(JsonField.of(clientId)) + + /** + * Sets [Builder.clientId] to an arbitrary JSON value. + * + * You should usually call [Builder.clientId] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun clientId(clientId: JsonField) = apply { + this.clientId = clientId + } + + fun clientX509CertUrl(clientX509CertUrl: String) = + clientX509CertUrl(JsonField.of(clientX509CertUrl)) + + /** + * Sets [Builder.clientX509CertUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.clientX509CertUrl] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun clientX509CertUrl(clientX509CertUrl: JsonField) = apply { + this.clientX509CertUrl = clientX509CertUrl + } + + fun privateKeyId(privateKeyId: String) = + privateKeyId(JsonField.of(privateKeyId)) + + /** + * Sets [Builder.privateKeyId] to an arbitrary JSON value. + * + * You should usually call [Builder.privateKeyId] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun privateKeyId(privateKeyId: JsonField) = apply { + this.privateKeyId = privateKeyId + } + + fun projectId(projectId: String) = projectId(JsonField.of(projectId)) + + /** + * Sets [Builder.projectId] to an arbitrary JSON value. + * + * You should usually call [Builder.projectId] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun projectId(projectId: JsonField) = apply { + this.projectId = projectId + } + + fun tokenUri(tokenUri: String) = tokenUri(JsonField.of(tokenUri)) + + /** + * Sets [Builder.tokenUri] to an arbitrary JSON value. + * + * You should usually call [Builder.tokenUri] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun tokenUri(tokenUri: JsonField) = apply { + this.tokenUri = tokenUri + } + + 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 universeDomain(universeDomain: String) = + universeDomain(JsonField.of(universeDomain)) + + /** + * Sets [Builder.universeDomain] to an arbitrary JSON value. + * + * You should usually call [Builder.universeDomain] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun universeDomain(universeDomain: JsonField) = apply { + this.universeDomain = universeDomain + } + + 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 [Credentials]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + * + * The following fields are required: + * ```java + * .clientEmail() + * .privateKey() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Credentials = + Credentials( + checkRequired("clientEmail", clientEmail), + checkRequired("privateKey", privateKey), + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Credentials = apply { + if (validated) { + return@apply + } + + clientEmail() + privateKey() + authProviderX509CertUrl() + authUri() + clientId() + clientX509CertUrl() + privateKeyId() + projectId() + tokenUri() + type().ifPresent { it.validate() } + universeDomain() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (clientEmail.asKnown().isPresent) 1 else 0) + + (if (privateKey.asKnown().isPresent) 1 else 0) + + (if (authProviderX509CertUrl.asKnown().isPresent) 1 else 0) + + (if (authUri.asKnown().isPresent) 1 else 0) + + (if (clientId.asKnown().isPresent) 1 else 0) + + (if (clientX509CertUrl.asKnown().isPresent) 1 else 0) + + (if (privateKeyId.asKnown().isPresent) 1 else 0) + + (if (projectId.asKnown().isPresent) 1 else 0) + + (if (tokenUri.asKnown().isPresent) 1 else 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + (if (universeDomain.asKnown().isPresent) 1 else 0) + + 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 SERVICE_ACCOUNT = of("service_account") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + SERVICE_ACCOUNT + } + + /** + * 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 { + SERVICE_ACCOUNT, + /** + * 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) { + SERVICE_ACCOUNT -> Value.SERVICE_ACCOUNT + 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 StagehandInvalidDataException if this class instance's value + * is a not a known member. + */ + fun known(): Known = + when (this) { + SERVICE_ACCOUNT -> Known.SERVICE_ACCOUNT + else -> + throw StagehandInvalidDataException("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 StagehandInvalidDataException if this class instance's value + * does not have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + StagehandInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API + * for existing fields. + * + * @throws StagehandInvalidDataException if any value type in this + * object doesn't match its expected type. + */ + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Type && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Credentials && + clientEmail == other.clientEmail && + privateKey == other.privateKey && + authProviderX509CertUrl == other.authProviderX509CertUrl && + authUri == other.authUri && + clientId == other.clientId && + clientX509CertUrl == other.clientX509CertUrl && + privateKeyId == other.privateKeyId && + projectId == other.projectId && + tokenUri == other.tokenUri && + type == other.type && + universeDomain == other.universeDomain && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + clientEmail, + privateKey, + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Credentials{clientEmail=$clientEmail, privateKey=$privateKey, authProviderX509CertUrl=$authProviderX509CertUrl, authUri=$authUri, clientId=$clientId, clientX509CertUrl=$clientX509CertUrl, privateKeyId=$privateKeyId, projectId=$projectId, tokenUri=$tokenUri, type=$type, universeDomain=$universeDomain, additionalProperties=$additionalProperties}" + } + + /** Google auth scopes for the desired API request */ + @JsonDeserialize(using = Scopes.Deserializer::class) + @JsonSerialize(using = Scopes.Serializer::class) + class Scopes + private constructor( + private val string: String? = null, + private val strings: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun strings(): Optional> = Optional.ofNullable(strings) + + fun isString(): Boolean = string != null + + fun isStrings(): Boolean = strings != null + + fun asString(): String = string.getOrThrow("string") + + fun asStrings(): List = strings.getOrThrow("strings") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the + * given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from + * the API, unless [visitor] overrides [Visitor.unknown]. To handle variants + * not known to this version of the SDK gracefully, consider overriding + * [Visitor.unknown]: + * ```java + * import com.browserbase.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = scopes.accept(new Scopes.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws StagehandInvalidDataException if [Visitor.unknown] is not + * overridden in [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + strings != null -> visitor.visitStrings(strings) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Scopes = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitStrings(strings: List) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 visitStrings(strings: List) = strings.size + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Scopes && + string == other.string && + strings == other.strings + } + + override fun hashCode(): Int = Objects.hash(string, strings) + + override fun toString(): String = + when { + string != null -> "Scopes{string=$string}" + strings != null -> "Scopes{strings=$strings}" + _json != null -> "Scopes{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Scopes") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Scopes(string = string) + + @JvmStatic + fun ofStrings(strings: List) = + Scopes(strings = strings.toImmutable()) + } + + /** + * An interface that defines how to map each variant of [Scopes] to a value + * of type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitStrings(strings: List): T + + /** + * Maps an unknown variant of [Scopes] to a value of type [T]. + * + * An instance of [Scopes] 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 StagehandInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw StagehandInvalidDataException("Unknown Scopes: $json") + } + } + + internal class Deserializer : BaseDeserializer(Scopes::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Scopes { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Scopes(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>()) + ?.let { Scopes(strings = 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 -> Scopes(_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(Scopes::class) { + + override fun serialize( + value: Scopes, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.strings != null -> generator.writeObject(value.strings) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Scopes") + } + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Auth && + credentials == other.credentials && + type == other.type && + projectId == other.projectId && + scopes == other.scopes && + universeDomain == other.universeDomain && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + credentials, + type, + projectId, + scopes, + universeDomain, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Auth{credentials=$credentials, type=$type, projectId=$projectId, scopes=$scopes, universeDomain=$universeDomain, additionalProperties=$additionalProperties}" + } + + /** Vertex provider-specific model configuration */ + class ProviderOptions + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val vertex: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("vertex") + @ExcludeMissing + vertex: JsonField = JsonMissing.of() + ) : this(vertex, mutableMapOf()) + + /** + * Vertex AI provider-specific settings + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with + * an unexpected value). + */ + fun vertex(): Vertex = vertex.getRequired("vertex") + + /** + * Returns the raw JSON value of [vertex]. + * + * Unlike [vertex], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("vertex") + @ExcludeMissing + fun _vertex(): JsonField = vertex + + @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 + * [ProviderOptions]. + * + * The following fields are required: + * ```java + * .vertex() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ProviderOptions]. */ + class Builder internal constructor() { + + private var vertex: JsonField? = null + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(providerOptions: ProviderOptions) = apply { + vertex = providerOptions.vertex + additionalProperties = + providerOptions.additionalProperties.toMutableMap() + } + + /** Vertex AI provider-specific settings */ + fun vertex(vertex: Vertex) = vertex(JsonField.of(vertex)) + + /** + * Sets [Builder.vertex] to an arbitrary JSON value. + * + * You should usually call [Builder.vertex] with a well-typed [Vertex] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun vertex(vertex: JsonField) = apply { this.vertex = vertex } + + 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 [ProviderOptions]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .vertex() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ProviderOptions = + ProviderOptions( + checkRequired("vertex", vertex), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): ProviderOptions = apply { + if (validated) { + return@apply + } + + vertex().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = (vertex.asKnown().getOrNull()?.validity() ?: 0) + + /** Vertex AI provider-specific settings */ + class Vertex + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val location: JsonField, + private val project: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("location") + @ExcludeMissing + location: JsonField = JsonMissing.of(), + @JsonProperty("project") + @ExcludeMissing + project: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + ) : this(location, project, baseUrl, headers, mutableMapOf()) + + /** + * Google Cloud location for Vertex AI models + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun location(): String = location.getRequired("location") + + /** + * Google Cloud project ID for Vertex AI models + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun project(): String = project.getRequired("project") + + /** + * Base URL for the Vertex AI provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the Vertex AI provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * Returns the raw JSON value of [location]. + * + * Unlike [location], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("location") + @ExcludeMissing + fun _location(): JsonField = location + + /** + * Returns the raw JSON value of [project]. + * + * Unlike [project], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("project") + @ExcludeMissing + fun _project(): JsonField = project + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("baseURL") + @ExcludeMissing + fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + @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 [Vertex]. + * + * The following fields are required: + * ```java + * .location() + * .project() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Vertex]. */ + class Builder internal constructor() { + + private var location: JsonField? = null + private var project: JsonField? = null + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(vertex: Vertex) = apply { + location = vertex.location + project = vertex.project + baseUrl = vertex.baseUrl + headers = vertex.headers + additionalProperties = vertex.additionalProperties.toMutableMap() + } + + /** Google Cloud location for Vertex AI models */ + fun location(location: String) = location(JsonField.of(location)) + + /** + * Sets [Builder.location] to an arbitrary JSON value. + * + * You should usually call [Builder.location] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun location(location: JsonField) = apply { + this.location = location + } + + /** Google Cloud project ID for Vertex AI models */ + fun project(project: String) = project(JsonField.of(project)) + + /** + * Sets [Builder.project] to an arbitrary JSON value. + * + * You should usually call [Builder.project] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun project(project: JsonField) = apply { + this.project = project + } + + /** Base URL for the Vertex AI provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { + this.baseUrl = baseUrl + } + + /** Custom headers sent with every request to the Vertex AI provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun headers(headers: JsonField) = apply { + this.headers = headers + } + + 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 [Vertex]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + * + * The following fields are required: + * ```java + * .location() + * .project() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Vertex = + Vertex( + checkRequired("location", location), + checkRequired("project", project), + baseUrl, + headers, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Vertex = apply { + if (validated) { + return@apply + } + + location() + project() + baseUrl() + headers().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (location.asKnown().isPresent) 1 else 0) + + (if (project.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + /** Custom headers sent with every request to the Vertex AI provider */ + class Headers + @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 + * [Headers]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = + headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API + * for existing fields. + * + * @throws StagehandInvalidDataException if any value type in this + * object doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Headers{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Vertex && + location == other.location && + project == other.project && + baseUrl == other.baseUrl && + headers == other.headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(location, project, baseUrl, headers, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Vertex{location=$location, project=$project, baseUrl=$baseUrl, headers=$headers, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ProviderOptions && + vertex == other.vertex && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(vertex, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ProviderOptions{vertex=$vertex, additionalProperties=$additionalProperties}" + } + + /** Custom headers sent with every request to the model provider */ + class Headers + @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 [Headers]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Headers{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is VertexModelConfigObject && + auth == other.auth && + modelName == other.modelName && + provider == other.provider && + providerOptions == other.providerOptions && + apiKey == other.apiKey && + baseUrl == other.baseUrl && + headers == other.headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + auth, + modelName, + provider, + providerOptions, + apiKey, + baseUrl, + headers, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "VertexModelConfigObject{auth=$auth, modelName=$modelName, provider=$provider, providerOptions=$providerOptions, apiKey=$apiKey, baseUrl=$baseUrl, headers=$headers, additionalProperties=$additionalProperties}" + } + + class GenericModelConfigObject + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val modelName: JsonField, + private val apiKey: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val provider: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("modelName") + @ExcludeMissing + modelName: JsonField = JsonMissing.of(), + @JsonProperty("apiKey") + @ExcludeMissing + apiKey: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + @JsonProperty("provider") + @ExcludeMissing + provider: JsonField = JsonMissing.of(), + ) : this(modelName, apiKey, baseUrl, headers, provider, mutableMapOf()) + + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun modelName(): String = modelName.getRequired("modelName") + + /** + * API key for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun apiKey(): Optional = apiKey.getOptional("apiKey") + + /** + * Base URL for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * AI provider for the model (or provide a baseURL endpoint instead) + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun provider(): Optional = provider.getOptional("provider") + + /** + * Returns the raw JSON value of [modelName]. + * + * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("modelName") + @ExcludeMissing + fun _modelName(): JsonField = modelName + + /** + * Returns the raw JSON value of [apiKey]. + * + * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + /** + * Returns the raw JSON value of [provider]. + * + * Unlike [provider], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("provider") + @ExcludeMissing + fun _provider(): JsonField = provider + + @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 + * [GenericModelConfigObject]. + * + * The following fields are required: + * ```java + * .modelName() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [GenericModelConfigObject]. */ + class Builder internal constructor() { + + private var modelName: JsonField? = null + private var apiKey: JsonField = JsonMissing.of() + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var provider: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(genericModelConfigObject: GenericModelConfigObject) = apply { + modelName = genericModelConfigObject.modelName + apiKey = genericModelConfigObject.apiKey + baseUrl = genericModelConfigObject.baseUrl + headers = genericModelConfigObject.headers + provider = genericModelConfigObject.provider + additionalProperties = + genericModelConfigObject.additionalProperties.toMutableMap() + } + + /** Model name string with provider prefix (e.g., 'openai/gpt-5-nano') */ + fun modelName(modelName: String) = modelName(JsonField.of(modelName)) + + /** + * Sets [Builder.modelName] to an arbitrary JSON value. + * + * You should usually call [Builder.modelName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun modelName(modelName: JsonField) = apply { + this.modelName = modelName + } + + /** API key for the model provider */ + fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + + /** + * Sets [Builder.apiKey] to an arbitrary JSON value. + * + * You should usually call [Builder.apiKey] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } + + /** Base URL for the model provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl } + + /** Custom headers sent with every request to the model provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun headers(headers: JsonField) = apply { this.headers = headers } + + /** AI provider for the model (or provide a baseURL endpoint instead) */ + fun provider(provider: Provider) = provider(JsonField.of(provider)) + + /** + * Sets [Builder.provider] to an arbitrary JSON value. + * + * You should usually call [Builder.provider] with a well-typed [Provider] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun provider(provider: JsonField) = apply { this.provider = provider } + + 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 [GenericModelConfigObject]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .modelName() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): GenericModelConfigObject = + GenericModelConfigObject( + checkRequired("modelName", modelName), + apiKey, + baseUrl, + headers, + provider, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): GenericModelConfigObject = apply { + if (validated) { + return@apply + } + + modelName() + apiKey() + baseUrl() + headers().ifPresent { it.validate() } + provider().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (modelName.asKnown().isPresent) 1 else 0) + + (if (apiKey.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + (provider.asKnown().getOrNull()?.validity() ?: 0) + + /** Custom headers sent with every request to the model provider */ + class Headers + @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 [Headers]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Headers{additionalProperties=$additionalProperties}" + } + + /** AI provider for the model (or provide a baseURL endpoint instead) */ + class Provider + @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 OPENAI = of("openai") + + @JvmField val ANTHROPIC = of("anthropic") + + @JvmField val GOOGLE = of("google") + + @JvmField val MICROSOFT = of("microsoft") + + @JvmField val BEDROCK = of("bedrock") + + @JvmStatic fun of(value: String) = Provider(JsonField.of(value)) + } + + /** An enum containing [Provider]'s known values. */ + enum class Known { + OPENAI, + ANTHROPIC, + GOOGLE, + MICROSOFT, + BEDROCK, + } + + /** + * An enum containing [Provider]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [Provider] 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 { + OPENAI, + ANTHROPIC, + GOOGLE, + MICROSOFT, + BEDROCK, + /** + * An enum member indicating that [Provider] 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) { + OPENAI -> Value.OPENAI + ANTHROPIC -> Value.ANTHROPIC + GOOGLE -> Value.GOOGLE + MICROSOFT -> Value.MICROSOFT + BEDROCK -> Value.BEDROCK + 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 StagehandInvalidDataException if this class instance's value is a not + * a known member. + */ + fun known(): Known = + when (this) { + OPENAI -> Known.OPENAI + ANTHROPIC -> Known.ANTHROPIC + GOOGLE -> Known.GOOGLE + MICROSOFT -> Known.MICROSOFT + BEDROCK -> Known.BEDROCK + else -> throw StagehandInvalidDataException("Unknown Provider: $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 StagehandInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + StagehandInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Provider = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Provider && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is GenericModelConfigObject && + modelName == other.modelName && + apiKey == other.apiKey && + baseUrl == other.baseUrl && + headers == other.headers && + provider == other.provider && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + modelName, + apiKey, + baseUrl, + headers, + provider, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "GenericModelConfigObject{modelName=$modelName, apiKey=$apiKey, baseUrl=$baseUrl, headers=$headers, provider=$provider, additionalProperties=$additionalProperties}" + } } /** diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteParams.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteParams.kt index d124ba0..3df5981 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteParams.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteParams.kt @@ -881,10 +881,18 @@ private constructor( } /** - * Alias for calling [executionModel] with `ExecutionModel.ofModelConfig(modelConfig)`. + * Alias for calling [executionModel] with + * `ExecutionModel.ofVertexModelConfigObject(vertexModelConfigObject)`. */ - fun executionModel(modelConfig: ModelConfig) = - executionModel(ExecutionModel.ofModelConfig(modelConfig)) + fun executionModel(vertexModelConfigObject: ExecutionModel.VertexModelConfigObject) = + executionModel(ExecutionModel.ofVertexModelConfigObject(vertexModelConfigObject)) + + /** + * Alias for calling [executionModel] with + * `ExecutionModel.ofGenericModelConfigObject(genericModelConfigObject)`. + */ + fun executionModel(genericModelConfigObject: ExecutionModel.GenericModelConfigObject) = + executionModel(ExecutionModel.ofGenericModelConfigObject(genericModelConfigObject)) /** Alias for calling [executionModel] with `ExecutionModel.ofString(string)`. */ fun executionModel(string: String) = executionModel(ExecutionModel.ofString(string)) @@ -913,8 +921,19 @@ private constructor( */ fun model(model: JsonField) = apply { this.model = model } - /** Alias for calling [model] with `Model.ofConfig(config)`. */ - fun model(config: ModelConfig) = model(Model.ofConfig(config)) + /** + * Alias for calling [model] with + * `Model.ofVertexModelConfigObject(vertexModelConfigObject)`. + */ + fun model(vertexModelConfigObject: Model.VertexModelConfigObject) = + model(Model.ofVertexModelConfigObject(vertexModelConfigObject)) + + /** + * Alias for calling [model] with + * `Model.ofGenericModelConfigObject(genericModelConfigObject)`. + */ + fun model(genericModelConfigObject: Model.GenericModelConfigObject) = + model(Model.ofGenericModelConfigObject(genericModelConfigObject)) /** Alias for calling [model] with `Model.ofString(string)`. */ fun model(string: String) = model(Model.ofString(string)) @@ -1038,20 +1057,31 @@ private constructor( @JsonSerialize(using = ExecutionModel.Serializer::class) class ExecutionModel private constructor( - private val modelConfig: ModelConfig? = null, + private val vertexModelConfigObject: VertexModelConfigObject? = null, + private val genericModelConfigObject: GenericModelConfigObject? = null, private val string: String? = null, private val _json: JsonValue? = null, ) { - fun modelConfig(): Optional = Optional.ofNullable(modelConfig) + fun vertexModelConfigObject(): Optional = + Optional.ofNullable(vertexModelConfigObject) + + fun genericModelConfigObject(): Optional = + Optional.ofNullable(genericModelConfigObject) fun string(): Optional = Optional.ofNullable(string) - fun isModelConfig(): Boolean = modelConfig != null + fun isVertexModelConfigObject(): Boolean = vertexModelConfigObject != null + + fun isGenericModelConfigObject(): Boolean = genericModelConfigObject != null fun isString(): Boolean = string != null - fun asModelConfig(): ModelConfig = modelConfig.getOrThrow("modelConfig") + fun asVertexModelConfigObject(): VertexModelConfigObject = + vertexModelConfigObject.getOrThrow("vertexModelConfigObject") + + fun asGenericModelConfigObject(): GenericModelConfigObject = + genericModelConfigObject.getOrThrow("genericModelConfigObject") fun asString(): String = string.getOrThrow("string") @@ -1070,8 +1100,8 @@ private constructor( * * Optional result = executionModel.accept(new ExecutionModel.Visitor>() { * @Override - * public Optional visitModelConfig(ModelConfig modelConfig) { - * return Optional.of(modelConfig.toString()); + * public Optional visitVertexModelConfigObject(VertexModelConfigObject vertexModelConfigObject) { + * return Optional.of(vertexModelConfigObject.toString()); * } * * // ... @@ -1089,7 +1119,10 @@ private constructor( */ fun accept(visitor: Visitor): T = when { - modelConfig != null -> visitor.visitModelConfig(modelConfig) + vertexModelConfigObject != null -> + visitor.visitVertexModelConfigObject(vertexModelConfigObject) + genericModelConfigObject != null -> + visitor.visitGenericModelConfigObject(genericModelConfigObject) string != null -> visitor.visitString(string) else -> visitor.unknown(_json) } @@ -1113,8 +1146,16 @@ private constructor( accept( object : Visitor { - override fun visitModelConfig(modelConfig: ModelConfig) { - modelConfig.validate() + override fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ) { + vertexModelConfigObject.validate() + } + + override fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ) { + genericModelConfigObject.validate() } override fun visitString(string: String) {} @@ -1141,8 +1182,13 @@ private constructor( internal fun validity(): Int = accept( object : Visitor { - override fun visitModelConfig(modelConfig: ModelConfig) = - modelConfig.validity() + override fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ) = vertexModelConfigObject.validity() + + override fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ) = genericModelConfigObject.validity() override fun visitString(string: String) = 1 @@ -1156,15 +1202,20 @@ private constructor( } return other is ExecutionModel && - modelConfig == other.modelConfig && + vertexModelConfigObject == other.vertexModelConfigObject && + genericModelConfigObject == other.genericModelConfigObject && string == other.string } - override fun hashCode(): Int = Objects.hash(modelConfig, string) + override fun hashCode(): Int = + Objects.hash(vertexModelConfigObject, genericModelConfigObject, string) override fun toString(): String = when { - modelConfig != null -> "ExecutionModel{modelConfig=$modelConfig}" + vertexModelConfigObject != null -> + "ExecutionModel{vertexModelConfigObject=$vertexModelConfigObject}" + genericModelConfigObject != null -> + "ExecutionModel{genericModelConfigObject=$genericModelConfigObject}" string != null -> "ExecutionModel{string=$string}" _json != null -> "ExecutionModel{_unknown=$_json}" else -> throw IllegalStateException("Invalid ExecutionModel") @@ -1173,8 +1224,12 @@ private constructor( companion object { @JvmStatic - fun ofModelConfig(modelConfig: ModelConfig) = - ExecutionModel(modelConfig = modelConfig) + fun ofVertexModelConfigObject(vertexModelConfigObject: VertexModelConfigObject) = + ExecutionModel(vertexModelConfigObject = vertexModelConfigObject) + + @JvmStatic + fun ofGenericModelConfigObject(genericModelConfigObject: GenericModelConfigObject) = + ExecutionModel(genericModelConfigObject = genericModelConfigObject) @JvmStatic fun ofString(string: String) = ExecutionModel(string = string) } @@ -1185,7 +1240,13 @@ private constructor( */ interface Visitor { - fun visitModelConfig(modelConfig: ModelConfig): T + fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ): T + + fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ): T fun visitString(string: String): T @@ -1211,9 +1272,14 @@ private constructor( val bestMatches = sequenceOf( - tryDeserialize(node, jacksonTypeRef())?.let { - ExecutionModel(modelConfig = it, _json = json) - }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { + ExecutionModel(vertexModelConfigObject = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { + ExecutionModel(genericModelConfigObject = it, _json = json) + }, tryDeserialize(node, jacksonTypeRef())?.let { ExecutionModel(string = it, _json = json) }, @@ -1242,368 +1308,6748 @@ private constructor( provider: SerializerProvider, ) { when { - value.modelConfig != null -> generator.writeObject(value.modelConfig) + value.vertexModelConfigObject != null -> + generator.writeObject(value.vertexModelConfigObject) + value.genericModelConfigObject != null -> + generator.writeObject(value.genericModelConfigObject) value.string != null -> generator.writeObject(value.string) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid ExecutionModel") } } } - } - /** Tool mode for the agent (dom, hybrid, cua). If set, overrides cua. */ - class Mode @JsonCreator private constructor(private val value: JsonField) : Enum { + class VertexModelConfigObject + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val auth: JsonField, + private val modelName: JsonField, + private val provider: JsonValue, + private val providerOptions: JsonField, + private val apiKey: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("auth") @ExcludeMissing auth: JsonField = JsonMissing.of(), + @JsonProperty("modelName") + @ExcludeMissing + modelName: JsonField = JsonMissing.of(), + @JsonProperty("provider") + @ExcludeMissing + provider: JsonValue = JsonMissing.of(), + @JsonProperty("providerOptions") + @ExcludeMissing + providerOptions: JsonField = JsonMissing.of(), + @JsonProperty("apiKey") + @ExcludeMissing + apiKey: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + ) : this( + auth, + modelName, + provider, + providerOptions, + apiKey, + baseUrl, + headers, + mutableMapOf(), + ) - /** - * 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 + /** + * Vertex provider authentication configuration + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun auth(): Auth = auth.getRequired("auth") - companion object { + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun modelName(): String = modelName.getRequired("modelName") - @JvmField val DOM = of("dom") + /** + * Vertex AI model provider + * + * Expected to always return the following: + * ```java + * JsonValue.from("vertex") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonValue = provider - @JvmField val HYBRID = of("hybrid") + /** + * Vertex provider-specific model configuration + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun providerOptions(): ProviderOptions = + providerOptions.getRequired("providerOptions") - @JvmField val CUA = of("cua") + /** + * API key for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun apiKey(): Optional = apiKey.getOptional("apiKey") - @JvmStatic fun of(value: String) = Mode(JsonField.of(value)) - } + /** + * Base URL for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") - /** An enum containing [Mode]'s known values. */ - enum class Known { - DOM, - HYBRID, - CUA, - } + /** + * Custom headers sent with every request to the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") - /** - * An enum containing [Mode]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Mode] 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 { - DOM, - HYBRID, - CUA, - /** An enum member indicating that [Mode] was instantiated with an unknown value. */ - _UNKNOWN, - } + /** + * Returns the raw JSON value of [auth]. + * + * Unlike [auth], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("auth") @ExcludeMissing fun _auth(): JsonField = auth - /** - * 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) { - DOM -> Value.DOM - HYBRID -> Value.HYBRID - CUA -> Value.CUA - else -> Value._UNKNOWN - } + /** + * Returns the raw JSON value of [modelName]. + * + * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("modelName") + @ExcludeMissing + fun _modelName(): JsonField = modelName - /** - * 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 StagehandInvalidDataException if this class instance's value is a not a known - * member. - */ - fun known(): Known = - when (this) { - DOM -> Known.DOM - HYBRID -> Known.HYBRID - CUA -> Known.CUA - else -> throw StagehandInvalidDataException("Unknown Mode: $value") + /** + * Returns the raw JSON value of [providerOptions]. + * + * Unlike [providerOptions], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("providerOptions") + @ExcludeMissing + fun _providerOptions(): JsonField = providerOptions + + /** + * Returns the raw JSON value of [apiKey]. + * + * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) } - /** - * Returns this class instance's primitive wire representation. - * - * This differs from the [toString] method because that method is primarily for - * debugging and generally doesn't throw. - * - * @throws StagehandInvalidDataException if this class instance's value does not have - * the expected primitive type. - */ - fun asString(): String = - _value().asString().orElseThrow { - StagehandInvalidDataException("Value is not a String") + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [VertexModelConfigObject]. + * + * The following fields are required: + * ```java + * .auth() + * .modelName() + * .providerOptions() + * ``` + */ + @JvmStatic fun builder() = Builder() } - private var validated: Boolean = false + /** A builder for [VertexModelConfigObject]. */ + class Builder internal constructor() { + + private var auth: JsonField? = null + private var modelName: JsonField? = null + private var provider: JsonValue = JsonValue.from("vertex") + private var providerOptions: JsonField? = null + private var apiKey: JsonField = JsonMissing.of() + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(vertexModelConfigObject: VertexModelConfigObject) = apply { + auth = vertexModelConfigObject.auth + modelName = vertexModelConfigObject.modelName + provider = vertexModelConfigObject.provider + providerOptions = vertexModelConfigObject.providerOptions + apiKey = vertexModelConfigObject.apiKey + baseUrl = vertexModelConfigObject.baseUrl + headers = vertexModelConfigObject.headers + additionalProperties = + vertexModelConfigObject.additionalProperties.toMutableMap() + } - /** - * Validates that the types of all values in this object match their expected types - * recursively. - * - * This method is _not_ forwards compatible with new types from the API for existing - * fields. - * - * @throws StagehandInvalidDataException if any value type in this object doesn't match - * its expected type. - */ - fun validate(): Mode = apply { - if (validated) { - return@apply - } + /** Vertex provider authentication configuration */ + fun auth(auth: Auth) = auth(JsonField.of(auth)) + + /** + * Sets [Builder.auth] to an arbitrary JSON value. + * + * You should usually call [Builder.auth] with a well-typed [Auth] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun auth(auth: JsonField) = apply { this.auth = auth } + + /** Model name string with provider prefix (e.g., 'openai/gpt-5-nano') */ + fun modelName(modelName: String) = modelName(JsonField.of(modelName)) + + /** + * Sets [Builder.modelName] to an arbitrary JSON value. + * + * You should usually call [Builder.modelName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun modelName(modelName: JsonField) = apply { + this.modelName = modelName + } - known() - validated = true - } + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to + * the following: + * ```java + * JsonValue.from("vertex") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun provider(provider: JsonValue) = apply { this.provider = provider } + + /** Vertex provider-specific model configuration */ + fun providerOptions(providerOptions: ProviderOptions) = + providerOptions(JsonField.of(providerOptions)) + + /** + * Sets [Builder.providerOptions] to an arbitrary JSON value. + * + * You should usually call [Builder.providerOptions] with a well-typed + * [ProviderOptions] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun providerOptions(providerOptions: JsonField) = apply { + this.providerOptions = providerOptions + } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: StagehandInvalidDataException) { - false + /** API key for the model provider */ + fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + + /** + * Sets [Builder.apiKey] to an arbitrary JSON value. + * + * You should usually call [Builder.apiKey] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } + + /** Base URL for the model provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl } + + /** Custom headers sent with every request to the model provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun headers(headers: JsonField) = apply { this.headers = headers } + + 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 [VertexModelConfigObject]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .auth() + * .modelName() + * .providerOptions() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): VertexModelConfigObject = + VertexModelConfigObject( + checkRequired("auth", auth), + checkRequired("modelName", modelName), + provider, + checkRequired("providerOptions", providerOptions), + apiKey, + baseUrl, + headers, + additionalProperties.toMutableMap(), + ) } - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + private var validated: Boolean = false - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): VertexModelConfigObject = apply { + if (validated) { + return@apply + } + + auth().validate() + modelName() + _provider().let { + if (it != JsonValue.from("vertex")) { + throw StagehandInvalidDataException( + "'provider' is invalid, received $it" + ) + } + } + providerOptions().validate() + apiKey() + baseUrl() + headers().ifPresent { it.validate() } + validated = true } - return other is Mode && value == other.value - } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + false + } - override fun hashCode() = value.hashCode() + /** + * 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 = + (auth.asKnown().getOrNull()?.validity() ?: 0) + + (if (modelName.asKnown().isPresent) 1 else 0) + + provider.let { if (it == JsonValue.from("vertex")) 1 else 0 } + + (providerOptions.asKnown().getOrNull()?.validity() ?: 0) + + (if (apiKey.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + /** Vertex provider authentication configuration */ + class Auth + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val credentials: JsonField, + private val type: JsonValue, + private val projectId: JsonField, + private val scopes: JsonField, + private val universeDomain: JsonField, + private val additionalProperties: MutableMap, + ) { - override fun toString() = value.toString() - } + @JsonCreator + private constructor( + @JsonProperty("credentials") + @ExcludeMissing + credentials: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("projectId") + @ExcludeMissing + projectId: JsonField = JsonMissing.of(), + @JsonProperty("scopes") + @ExcludeMissing + scopes: JsonField = JsonMissing.of(), + @JsonProperty("universeDomain") + @ExcludeMissing + universeDomain: JsonField = JsonMissing.of(), + ) : this(credentials, type, projectId, scopes, universeDomain, mutableMapOf()) + + /** + * Google Cloud service account credentials + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with + * an unexpected value). + */ + fun credentials(): Credentials = credentials.getRequired("credentials") + + /** + * Use inline Google Cloud service account credentials for provider + * authentication + * + * Expected to always return the following: + * ```java + * JsonValue.from("googleServiceAccount") + * ``` + * + * 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 + + /** + * Google Cloud project ID used by google-auth-library + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun projectId(): Optional = projectId.getOptional("projectId") + + /** + * Google auth scopes for the desired API request + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun scopes(): Optional = scopes.getOptional("scopes") + + /** + * Google Cloud universe domain + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun universeDomain(): Optional = + universeDomain.getOptional("universeDomain") + + /** + * Returns the raw JSON value of [credentials]. + * + * Unlike [credentials], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("credentials") + @ExcludeMissing + fun _credentials(): JsonField = credentials + + /** + * Returns the raw JSON value of [projectId]. + * + * Unlike [projectId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("projectId") + @ExcludeMissing + fun _projectId(): JsonField = projectId + + /** + * Returns the raw JSON value of [scopes]. + * + * Unlike [scopes], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("scopes") + @ExcludeMissing + fun _scopes(): JsonField = scopes + + /** + * Returns the raw JSON value of [universeDomain]. + * + * Unlike [universeDomain], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("universeDomain") + @ExcludeMissing + fun _universeDomain(): JsonField = universeDomain + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } - /** Model configuration object or model name string (e.g., 'openai/gpt-5-nano') */ - @JsonDeserialize(using = Model.Deserializer::class) - @JsonSerialize(using = Model.Serializer::class) - class Model - private constructor( - private val config: ModelConfig? = null, - private val string: String? = null, - private val _json: JsonValue? = null, - ) { + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - fun config(): Optional = Optional.ofNullable(config) + fun toBuilder() = Builder().from(this) - fun string(): Optional = Optional.ofNullable(string) + companion object { - fun isConfig(): Boolean = config != null + /** + * Returns a mutable builder for constructing an instance of [Auth]. + * + * The following fields are required: + * ```java + * .credentials() + * ``` + */ + @JvmStatic fun builder() = Builder() + } - fun isString(): Boolean = string != null + /** A builder for [Auth]. */ + class Builder internal constructor() { + + private var credentials: JsonField? = null + private var type: JsonValue = JsonValue.from("googleServiceAccount") + private var projectId: JsonField = JsonMissing.of() + private var scopes: JsonField = JsonMissing.of() + private var universeDomain: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(auth: Auth) = apply { + credentials = auth.credentials + type = auth.type + projectId = auth.projectId + scopes = auth.scopes + universeDomain = auth.universeDomain + additionalProperties = auth.additionalProperties.toMutableMap() + } - fun asConfig(): ModelConfig = config.getOrThrow("config") + /** Google Cloud service account credentials */ + fun credentials(credentials: Credentials) = + credentials(JsonField.of(credentials)) + + /** + * Sets [Builder.credentials] to an arbitrary JSON value. + * + * You should usually call [Builder.credentials] with a well-typed + * [Credentials] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun credentials(credentials: JsonField) = apply { + this.credentials = credentials + } - fun asString(): String = string.getOrThrow("string") + /** + * 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("googleServiceAccount") + * ``` + * + * This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** Google Cloud project ID used by google-auth-library */ + fun projectId(projectId: String) = projectId(JsonField.of(projectId)) + + /** + * Sets [Builder.projectId] to an arbitrary JSON value. + * + * You should usually call [Builder.projectId] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun projectId(projectId: JsonField) = apply { + this.projectId = projectId + } - fun _json(): Optional = Optional.ofNullable(_json) + /** Google auth scopes for the desired API request */ + fun scopes(scopes: Scopes) = scopes(JsonField.of(scopes)) + + /** + * Sets [Builder.scopes] to an arbitrary JSON value. + * + * You should usually call [Builder.scopes] with a well-typed [Scopes] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun scopes(scopes: JsonField) = apply { this.scopes = scopes } + + /** Alias for calling [scopes] with `Scopes.ofString(string)`. */ + fun scopes(string: String) = scopes(Scopes.ofString(string)) + + /** Alias for calling [scopes] with `Scopes.ofStrings(strings)`. */ + fun scopesOfStrings(strings: List) = + scopes(Scopes.ofStrings(strings)) + + /** Google Cloud universe domain */ + fun universeDomain(universeDomain: String) = + universeDomain(JsonField.of(universeDomain)) + + /** + * Sets [Builder.universeDomain] to an arbitrary JSON value. + * + * You should usually call [Builder.universeDomain] with a well-typed + * [String] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun universeDomain(universeDomain: JsonField) = apply { + this.universeDomain = universeDomain + } - /** - * Maps this instance's current variant to a value of type [T] using the given - * [visitor]. - * - * Note that this method is _not_ forwards compatible with new variants from the API, - * unless [visitor] overrides [Visitor.unknown]. To handle variants not known to this - * version of the SDK gracefully, consider overriding [Visitor.unknown]: - * ```java - * import com.browserbase.api.core.JsonValue; - * import java.util.Optional; - * - * Optional result = model.accept(new Model.Visitor>() { - * @Override - * public Optional visitConfig(ModelConfig config) { - * return Optional.of(config.toString()); - * } - * - * // ... - * - * @Override - * public Optional unknown(JsonValue json) { - * // Or inspect the `json`. - * return Optional.empty(); - * } - * }); - * ``` - * + 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 [Auth]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .credentials() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Auth = + Auth( + checkRequired("credentials", credentials), + type, + projectId, + scopes, + universeDomain, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Auth = apply { + if (validated) { + return@apply + } + + credentials().validate() + _type().let { + if (it != JsonValue.from("googleServiceAccount")) { + throw StagehandInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + projectId() + scopes().ifPresent { it.validate() } + universeDomain() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = + (credentials.asKnown().getOrNull()?.validity() ?: 0) + + type.let { + if (it == JsonValue.from("googleServiceAccount")) 1 else 0 + } + + (if (projectId.asKnown().isPresent) 1 else 0) + + (scopes.asKnown().getOrNull()?.validity() ?: 0) + + (if (universeDomain.asKnown().isPresent) 1 else 0) + + /** Google Cloud service account credentials */ + class Credentials + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val clientEmail: JsonField, + private val privateKey: JsonField, + private val authProviderX509CertUrl: JsonField, + private val authUri: JsonField, + private val clientId: JsonField, + private val clientX509CertUrl: JsonField, + private val privateKeyId: JsonField, + private val projectId: JsonField, + private val tokenUri: JsonField, + private val type: JsonField, + private val universeDomain: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("client_email") + @ExcludeMissing + clientEmail: JsonField = JsonMissing.of(), + @JsonProperty("private_key") + @ExcludeMissing + privateKey: JsonField = JsonMissing.of(), + @JsonProperty("auth_provider_x509_cert_url") + @ExcludeMissing + authProviderX509CertUrl: JsonField = JsonMissing.of(), + @JsonProperty("auth_uri") + @ExcludeMissing + authUri: JsonField = JsonMissing.of(), + @JsonProperty("client_id") + @ExcludeMissing + clientId: JsonField = JsonMissing.of(), + @JsonProperty("client_x509_cert_url") + @ExcludeMissing + clientX509CertUrl: JsonField = JsonMissing.of(), + @JsonProperty("private_key_id") + @ExcludeMissing + privateKeyId: JsonField = JsonMissing.of(), + @JsonProperty("project_id") + @ExcludeMissing + projectId: JsonField = JsonMissing.of(), + @JsonProperty("token_uri") + @ExcludeMissing + tokenUri: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonField = JsonMissing.of(), + @JsonProperty("universe_domain") + @ExcludeMissing + universeDomain: JsonField = JsonMissing.of(), + ) : this( + clientEmail, + privateKey, + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + mutableMapOf(), + ) + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun clientEmail(): String = clientEmail.getRequired("client_email") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun privateKey(): String = privateKey.getRequired("private_key") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun authProviderX509CertUrl(): Optional = + authProviderX509CertUrl.getOptional("auth_provider_x509_cert_url") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun authUri(): Optional = authUri.getOptional("auth_uri") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun clientId(): Optional = clientId.getOptional("client_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun clientX509CertUrl(): Optional = + clientX509CertUrl.getOptional("client_x509_cert_url") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun privateKeyId(): Optional = + privateKeyId.getOptional("private_key_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun projectId(): Optional = projectId.getOptional("project_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun tokenUri(): Optional = tokenUri.getOptional("token_uri") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun universeDomain(): Optional = + universeDomain.getOptional("universe_domain") + + /** + * Returns the raw JSON value of [clientEmail]. + * + * Unlike [clientEmail], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("client_email") + @ExcludeMissing + fun _clientEmail(): JsonField = clientEmail + + /** + * Returns the raw JSON value of [privateKey]. + * + * Unlike [privateKey], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("private_key") + @ExcludeMissing + fun _privateKey(): JsonField = privateKey + + /** + * Returns the raw JSON value of [authProviderX509CertUrl]. + * + * Unlike [authProviderX509CertUrl], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("auth_provider_x509_cert_url") + @ExcludeMissing + fun _authProviderX509CertUrl(): JsonField = authProviderX509CertUrl + + /** + * Returns the raw JSON value of [authUri]. + * + * Unlike [authUri], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("auth_uri") + @ExcludeMissing + fun _authUri(): JsonField = authUri + + /** + * Returns the raw JSON value of [clientId]. + * + * Unlike [clientId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("client_id") + @ExcludeMissing + fun _clientId(): JsonField = clientId + + /** + * Returns the raw JSON value of [clientX509CertUrl]. + * + * Unlike [clientX509CertUrl], this method doesn't throw if the JSON field + * has an unexpected type. + */ + @JsonProperty("client_x509_cert_url") + @ExcludeMissing + fun _clientX509CertUrl(): JsonField = clientX509CertUrl + + /** + * Returns the raw JSON value of [privateKeyId]. + * + * Unlike [privateKeyId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("private_key_id") + @ExcludeMissing + fun _privateKeyId(): JsonField = privateKeyId + + /** + * Returns the raw JSON value of [projectId]. + * + * Unlike [projectId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("project_id") + @ExcludeMissing + fun _projectId(): JsonField = projectId + + /** + * Returns the raw JSON value of [tokenUri]. + * + * Unlike [tokenUri], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("token_uri") + @ExcludeMissing + fun _tokenUri(): JsonField = tokenUri + + /** + * 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 [universeDomain]. + * + * Unlike [universeDomain], this method doesn't throw if the JSON field has + * an unexpected type. + */ + @JsonProperty("universe_domain") + @ExcludeMissing + fun _universeDomain(): JsonField = universeDomain + + @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 + * [Credentials]. + * + * The following fields are required: + * ```java + * .clientEmail() + * .privateKey() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Credentials]. */ + class Builder internal constructor() { + + private var clientEmail: JsonField? = null + private var privateKey: JsonField? = null + private var authProviderX509CertUrl: JsonField = + JsonMissing.of() + private var authUri: JsonField = JsonMissing.of() + private var clientId: JsonField = JsonMissing.of() + private var clientX509CertUrl: JsonField = JsonMissing.of() + private var privateKeyId: JsonField = JsonMissing.of() + private var projectId: JsonField = JsonMissing.of() + private var tokenUri: JsonField = JsonMissing.of() + private var type: JsonField = JsonMissing.of() + private var universeDomain: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(credentials: Credentials) = apply { + clientEmail = credentials.clientEmail + privateKey = credentials.privateKey + authProviderX509CertUrl = credentials.authProviderX509CertUrl + authUri = credentials.authUri + clientId = credentials.clientId + clientX509CertUrl = credentials.clientX509CertUrl + privateKeyId = credentials.privateKeyId + projectId = credentials.projectId + tokenUri = credentials.tokenUri + type = credentials.type + universeDomain = credentials.universeDomain + additionalProperties = + credentials.additionalProperties.toMutableMap() + } + + fun clientEmail(clientEmail: String) = + clientEmail(JsonField.of(clientEmail)) + + /** + * Sets [Builder.clientEmail] to an arbitrary JSON value. + * + * You should usually call [Builder.clientEmail] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun clientEmail(clientEmail: JsonField) = apply { + this.clientEmail = clientEmail + } + + fun privateKey(privateKey: String) = + privateKey(JsonField.of(privateKey)) + + /** + * Sets [Builder.privateKey] to an arbitrary JSON value. + * + * You should usually call [Builder.privateKey] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun privateKey(privateKey: JsonField) = apply { + this.privateKey = privateKey + } + + fun authProviderX509CertUrl(authProviderX509CertUrl: String) = + authProviderX509CertUrl(JsonField.of(authProviderX509CertUrl)) + + /** + * Sets [Builder.authProviderX509CertUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.authProviderX509CertUrl] with a + * well-typed [String] value instead. This method is primarily for + * setting the field to an undocumented or not yet supported value. + */ + fun authProviderX509CertUrl( + authProviderX509CertUrl: JsonField + ) = apply { this.authProviderX509CertUrl = authProviderX509CertUrl } + + fun authUri(authUri: String) = authUri(JsonField.of(authUri)) + + /** + * Sets [Builder.authUri] to an arbitrary JSON value. + * + * You should usually call [Builder.authUri] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun authUri(authUri: JsonField) = apply { + this.authUri = authUri + } + + fun clientId(clientId: String) = clientId(JsonField.of(clientId)) + + /** + * Sets [Builder.clientId] to an arbitrary JSON value. + * + * You should usually call [Builder.clientId] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun clientId(clientId: JsonField) = apply { + this.clientId = clientId + } + + fun clientX509CertUrl(clientX509CertUrl: String) = + clientX509CertUrl(JsonField.of(clientX509CertUrl)) + + /** + * Sets [Builder.clientX509CertUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.clientX509CertUrl] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun clientX509CertUrl(clientX509CertUrl: JsonField) = apply { + this.clientX509CertUrl = clientX509CertUrl + } + + fun privateKeyId(privateKeyId: String) = + privateKeyId(JsonField.of(privateKeyId)) + + /** + * Sets [Builder.privateKeyId] to an arbitrary JSON value. + * + * You should usually call [Builder.privateKeyId] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun privateKeyId(privateKeyId: JsonField) = apply { + this.privateKeyId = privateKeyId + } + + fun projectId(projectId: String) = projectId(JsonField.of(projectId)) + + /** + * Sets [Builder.projectId] to an arbitrary JSON value. + * + * You should usually call [Builder.projectId] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun projectId(projectId: JsonField) = apply { + this.projectId = projectId + } + + fun tokenUri(tokenUri: String) = tokenUri(JsonField.of(tokenUri)) + + /** + * Sets [Builder.tokenUri] to an arbitrary JSON value. + * + * You should usually call [Builder.tokenUri] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun tokenUri(tokenUri: JsonField) = apply { + this.tokenUri = tokenUri + } + + 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 universeDomain(universeDomain: String) = + universeDomain(JsonField.of(universeDomain)) + + /** + * Sets [Builder.universeDomain] to an arbitrary JSON value. + * + * You should usually call [Builder.universeDomain] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun universeDomain(universeDomain: JsonField) = apply { + this.universeDomain = universeDomain + } + + 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 [Credentials]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + * + * The following fields are required: + * ```java + * .clientEmail() + * .privateKey() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Credentials = + Credentials( + checkRequired("clientEmail", clientEmail), + checkRequired("privateKey", privateKey), + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Credentials = apply { + if (validated) { + return@apply + } + + clientEmail() + privateKey() + authProviderX509CertUrl() + authUri() + clientId() + clientX509CertUrl() + privateKeyId() + projectId() + tokenUri() + type().ifPresent { it.validate() } + universeDomain() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (clientEmail.asKnown().isPresent) 1 else 0) + + (if (privateKey.asKnown().isPresent) 1 else 0) + + (if (authProviderX509CertUrl.asKnown().isPresent) 1 else 0) + + (if (authUri.asKnown().isPresent) 1 else 0) + + (if (clientId.asKnown().isPresent) 1 else 0) + + (if (clientX509CertUrl.asKnown().isPresent) 1 else 0) + + (if (privateKeyId.asKnown().isPresent) 1 else 0) + + (if (projectId.asKnown().isPresent) 1 else 0) + + (if (tokenUri.asKnown().isPresent) 1 else 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + (if (universeDomain.asKnown().isPresent) 1 else 0) + + 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 SERVICE_ACCOUNT = of("service_account") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + SERVICE_ACCOUNT + } + + /** + * 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 { + SERVICE_ACCOUNT, + /** + * 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) { + SERVICE_ACCOUNT -> Value.SERVICE_ACCOUNT + 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 StagehandInvalidDataException if this class instance's value + * is a not a known member. + */ + fun known(): Known = + when (this) { + SERVICE_ACCOUNT -> Known.SERVICE_ACCOUNT + else -> + throw StagehandInvalidDataException("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 StagehandInvalidDataException if this class instance's value + * does not have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + StagehandInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API + * for existing fields. + * + * @throws StagehandInvalidDataException if any value type in this + * object doesn't match its expected type. + */ + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Type && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Credentials && + clientEmail == other.clientEmail && + privateKey == other.privateKey && + authProviderX509CertUrl == other.authProviderX509CertUrl && + authUri == other.authUri && + clientId == other.clientId && + clientX509CertUrl == other.clientX509CertUrl && + privateKeyId == other.privateKeyId && + projectId == other.projectId && + tokenUri == other.tokenUri && + type == other.type && + universeDomain == other.universeDomain && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + clientEmail, + privateKey, + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Credentials{clientEmail=$clientEmail, privateKey=$privateKey, authProviderX509CertUrl=$authProviderX509CertUrl, authUri=$authUri, clientId=$clientId, clientX509CertUrl=$clientX509CertUrl, privateKeyId=$privateKeyId, projectId=$projectId, tokenUri=$tokenUri, type=$type, universeDomain=$universeDomain, additionalProperties=$additionalProperties}" + } + + /** Google auth scopes for the desired API request */ + @JsonDeserialize(using = Scopes.Deserializer::class) + @JsonSerialize(using = Scopes.Serializer::class) + class Scopes + private constructor( + private val string: String? = null, + private val strings: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun strings(): Optional> = Optional.ofNullable(strings) + + fun isString(): Boolean = string != null + + fun isStrings(): Boolean = strings != null + + fun asString(): String = string.getOrThrow("string") + + fun asStrings(): List = strings.getOrThrow("strings") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the + * given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from + * the API, unless [visitor] overrides [Visitor.unknown]. To handle variants + * not known to this version of the SDK gracefully, consider overriding + * [Visitor.unknown]: + * ```java + * import com.browserbase.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = scopes.accept(new Scopes.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws StagehandInvalidDataException if [Visitor.unknown] is not + * overridden in [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + strings != null -> visitor.visitStrings(strings) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Scopes = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitStrings(strings: List) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 visitStrings(strings: List) = strings.size + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Scopes && + string == other.string && + strings == other.strings + } + + override fun hashCode(): Int = Objects.hash(string, strings) + + override fun toString(): String = + when { + string != null -> "Scopes{string=$string}" + strings != null -> "Scopes{strings=$strings}" + _json != null -> "Scopes{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Scopes") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Scopes(string = string) + + @JvmStatic + fun ofStrings(strings: List) = + Scopes(strings = strings.toImmutable()) + } + + /** + * An interface that defines how to map each variant of [Scopes] to a value + * of type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitStrings(strings: List): T + + /** + * Maps an unknown variant of [Scopes] to a value of type [T]. + * + * An instance of [Scopes] 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 StagehandInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw StagehandInvalidDataException("Unknown Scopes: $json") + } + } + + internal class Deserializer : BaseDeserializer(Scopes::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Scopes { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Scopes(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>()) + ?.let { Scopes(strings = 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 -> Scopes(_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(Scopes::class) { + + override fun serialize( + value: Scopes, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.strings != null -> generator.writeObject(value.strings) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Scopes") + } + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Auth && + credentials == other.credentials && + type == other.type && + projectId == other.projectId && + scopes == other.scopes && + universeDomain == other.universeDomain && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + credentials, + type, + projectId, + scopes, + universeDomain, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Auth{credentials=$credentials, type=$type, projectId=$projectId, scopes=$scopes, universeDomain=$universeDomain, additionalProperties=$additionalProperties}" + } + + /** Vertex provider-specific model configuration */ + class ProviderOptions + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val vertex: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("vertex") + @ExcludeMissing + vertex: JsonField = JsonMissing.of() + ) : this(vertex, mutableMapOf()) + + /** + * Vertex AI provider-specific settings + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with + * an unexpected value). + */ + fun vertex(): Vertex = vertex.getRequired("vertex") + + /** + * Returns the raw JSON value of [vertex]. + * + * Unlike [vertex], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("vertex") + @ExcludeMissing + fun _vertex(): JsonField = vertex + + @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 + * [ProviderOptions]. + * + * The following fields are required: + * ```java + * .vertex() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ProviderOptions]. */ + class Builder internal constructor() { + + private var vertex: JsonField? = null + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(providerOptions: ProviderOptions) = apply { + vertex = providerOptions.vertex + additionalProperties = + providerOptions.additionalProperties.toMutableMap() + } + + /** Vertex AI provider-specific settings */ + fun vertex(vertex: Vertex) = vertex(JsonField.of(vertex)) + + /** + * Sets [Builder.vertex] to an arbitrary JSON value. + * + * You should usually call [Builder.vertex] with a well-typed [Vertex] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun vertex(vertex: JsonField) = apply { this.vertex = vertex } + + 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 [ProviderOptions]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .vertex() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ProviderOptions = + ProviderOptions( + checkRequired("vertex", vertex), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): ProviderOptions = apply { + if (validated) { + return@apply + } + + vertex().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = (vertex.asKnown().getOrNull()?.validity() ?: 0) + + /** Vertex AI provider-specific settings */ + class Vertex + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val location: JsonField, + private val project: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("location") + @ExcludeMissing + location: JsonField = JsonMissing.of(), + @JsonProperty("project") + @ExcludeMissing + project: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + ) : this(location, project, baseUrl, headers, mutableMapOf()) + + /** + * Google Cloud location for Vertex AI models + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun location(): String = location.getRequired("location") + + /** + * Google Cloud project ID for Vertex AI models + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun project(): String = project.getRequired("project") + + /** + * Base URL for the Vertex AI provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the Vertex AI provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * Returns the raw JSON value of [location]. + * + * Unlike [location], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("location") + @ExcludeMissing + fun _location(): JsonField = location + + /** + * Returns the raw JSON value of [project]. + * + * Unlike [project], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("project") + @ExcludeMissing + fun _project(): JsonField = project + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("baseURL") + @ExcludeMissing + fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + @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 [Vertex]. + * + * The following fields are required: + * ```java + * .location() + * .project() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Vertex]. */ + class Builder internal constructor() { + + private var location: JsonField? = null + private var project: JsonField? = null + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(vertex: Vertex) = apply { + location = vertex.location + project = vertex.project + baseUrl = vertex.baseUrl + headers = vertex.headers + additionalProperties = vertex.additionalProperties.toMutableMap() + } + + /** Google Cloud location for Vertex AI models */ + fun location(location: String) = location(JsonField.of(location)) + + /** + * Sets [Builder.location] to an arbitrary JSON value. + * + * You should usually call [Builder.location] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun location(location: JsonField) = apply { + this.location = location + } + + /** Google Cloud project ID for Vertex AI models */ + fun project(project: String) = project(JsonField.of(project)) + + /** + * Sets [Builder.project] to an arbitrary JSON value. + * + * You should usually call [Builder.project] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun project(project: JsonField) = apply { + this.project = project + } + + /** Base URL for the Vertex AI provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { + this.baseUrl = baseUrl + } + + /** Custom headers sent with every request to the Vertex AI provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun headers(headers: JsonField) = apply { + this.headers = headers + } + + 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 [Vertex]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + * + * The following fields are required: + * ```java + * .location() + * .project() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Vertex = + Vertex( + checkRequired("location", location), + checkRequired("project", project), + baseUrl, + headers, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Vertex = apply { + if (validated) { + return@apply + } + + location() + project() + baseUrl() + headers().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (location.asKnown().isPresent) 1 else 0) + + (if (project.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + /** Custom headers sent with every request to the Vertex AI provider */ + class Headers + @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 + * [Headers]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = + headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API + * for existing fields. + * + * @throws StagehandInvalidDataException if any value type in this + * object doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Headers{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Vertex && + location == other.location && + project == other.project && + baseUrl == other.baseUrl && + headers == other.headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(location, project, baseUrl, headers, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Vertex{location=$location, project=$project, baseUrl=$baseUrl, headers=$headers, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ProviderOptions && + vertex == other.vertex && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(vertex, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ProviderOptions{vertex=$vertex, additionalProperties=$additionalProperties}" + } + + /** Custom headers sent with every request to the model provider */ + class Headers + @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 [Headers]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Headers{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is VertexModelConfigObject && + auth == other.auth && + modelName == other.modelName && + provider == other.provider && + providerOptions == other.providerOptions && + apiKey == other.apiKey && + baseUrl == other.baseUrl && + headers == other.headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + auth, + modelName, + provider, + providerOptions, + apiKey, + baseUrl, + headers, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "VertexModelConfigObject{auth=$auth, modelName=$modelName, provider=$provider, providerOptions=$providerOptions, apiKey=$apiKey, baseUrl=$baseUrl, headers=$headers, additionalProperties=$additionalProperties}" + } + + class GenericModelConfigObject + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val modelName: JsonField, + private val apiKey: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val provider: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("modelName") + @ExcludeMissing + modelName: JsonField = JsonMissing.of(), + @JsonProperty("apiKey") + @ExcludeMissing + apiKey: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + @JsonProperty("provider") + @ExcludeMissing + provider: JsonField = JsonMissing.of(), + ) : this(modelName, apiKey, baseUrl, headers, provider, mutableMapOf()) + + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun modelName(): String = modelName.getRequired("modelName") + + /** + * API key for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun apiKey(): Optional = apiKey.getOptional("apiKey") + + /** + * Base URL for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * AI provider for the model (or provide a baseURL endpoint instead) + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun provider(): Optional = provider.getOptional("provider") + + /** + * Returns the raw JSON value of [modelName]. + * + * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("modelName") + @ExcludeMissing + fun _modelName(): JsonField = modelName + + /** + * Returns the raw JSON value of [apiKey]. + * + * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + /** + * Returns the raw JSON value of [provider]. + * + * Unlike [provider], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("provider") + @ExcludeMissing + fun _provider(): JsonField = provider + + @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 + * [GenericModelConfigObject]. + * + * The following fields are required: + * ```java + * .modelName() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [GenericModelConfigObject]. */ + class Builder internal constructor() { + + private var modelName: JsonField? = null + private var apiKey: JsonField = JsonMissing.of() + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var provider: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(genericModelConfigObject: GenericModelConfigObject) = apply { + modelName = genericModelConfigObject.modelName + apiKey = genericModelConfigObject.apiKey + baseUrl = genericModelConfigObject.baseUrl + headers = genericModelConfigObject.headers + provider = genericModelConfigObject.provider + additionalProperties = + genericModelConfigObject.additionalProperties.toMutableMap() + } + + /** Model name string with provider prefix (e.g., 'openai/gpt-5-nano') */ + fun modelName(modelName: String) = modelName(JsonField.of(modelName)) + + /** + * Sets [Builder.modelName] to an arbitrary JSON value. + * + * You should usually call [Builder.modelName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun modelName(modelName: JsonField) = apply { + this.modelName = modelName + } + + /** API key for the model provider */ + fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + + /** + * Sets [Builder.apiKey] to an arbitrary JSON value. + * + * You should usually call [Builder.apiKey] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } + + /** Base URL for the model provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl } + + /** Custom headers sent with every request to the model provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun headers(headers: JsonField) = apply { this.headers = headers } + + /** AI provider for the model (or provide a baseURL endpoint instead) */ + fun provider(provider: Provider) = provider(JsonField.of(provider)) + + /** + * Sets [Builder.provider] to an arbitrary JSON value. + * + * You should usually call [Builder.provider] with a well-typed [Provider] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun provider(provider: JsonField) = apply { this.provider = provider } + + 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 [GenericModelConfigObject]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .modelName() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): GenericModelConfigObject = + GenericModelConfigObject( + checkRequired("modelName", modelName), + apiKey, + baseUrl, + headers, + provider, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): GenericModelConfigObject = apply { + if (validated) { + return@apply + } + + modelName() + apiKey() + baseUrl() + headers().ifPresent { it.validate() } + provider().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (modelName.asKnown().isPresent) 1 else 0) + + (if (apiKey.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + (provider.asKnown().getOrNull()?.validity() ?: 0) + + /** Custom headers sent with every request to the model provider */ + class Headers + @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 [Headers]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Headers{additionalProperties=$additionalProperties}" + } + + /** AI provider for the model (or provide a baseURL endpoint instead) */ + class Provider + @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 OPENAI = of("openai") + + @JvmField val ANTHROPIC = of("anthropic") + + @JvmField val GOOGLE = of("google") + + @JvmField val MICROSOFT = of("microsoft") + + @JvmField val BEDROCK = of("bedrock") + + @JvmStatic fun of(value: String) = Provider(JsonField.of(value)) + } + + /** An enum containing [Provider]'s known values. */ + enum class Known { + OPENAI, + ANTHROPIC, + GOOGLE, + MICROSOFT, + BEDROCK, + } + + /** + * An enum containing [Provider]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [Provider] 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 { + OPENAI, + ANTHROPIC, + GOOGLE, + MICROSOFT, + BEDROCK, + /** + * An enum member indicating that [Provider] 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) { + OPENAI -> Value.OPENAI + ANTHROPIC -> Value.ANTHROPIC + GOOGLE -> Value.GOOGLE + MICROSOFT -> Value.MICROSOFT + BEDROCK -> Value.BEDROCK + 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 StagehandInvalidDataException if this class instance's value is a not + * a known member. + */ + fun known(): Known = + when (this) { + OPENAI -> Known.OPENAI + ANTHROPIC -> Known.ANTHROPIC + GOOGLE -> Known.GOOGLE + MICROSOFT -> Known.MICROSOFT + BEDROCK -> Known.BEDROCK + else -> throw StagehandInvalidDataException("Unknown Provider: $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 StagehandInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + StagehandInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Provider = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Provider && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is GenericModelConfigObject && + modelName == other.modelName && + apiKey == other.apiKey && + baseUrl == other.baseUrl && + headers == other.headers && + provider == other.provider && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + modelName, + apiKey, + baseUrl, + headers, + provider, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "GenericModelConfigObject{modelName=$modelName, apiKey=$apiKey, baseUrl=$baseUrl, headers=$headers, provider=$provider, additionalProperties=$additionalProperties}" + } + } + + /** Tool mode for the agent (dom, hybrid, cua). If set, overrides cua. */ + class Mode @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 DOM = of("dom") + + @JvmField val HYBRID = of("hybrid") + + @JvmField val CUA = of("cua") + + @JvmStatic fun of(value: String) = Mode(JsonField.of(value)) + } + + /** An enum containing [Mode]'s known values. */ + enum class Known { + DOM, + HYBRID, + CUA, + } + + /** + * An enum containing [Mode]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Mode] 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 { + DOM, + HYBRID, + CUA, + /** An enum member indicating that [Mode] 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) { + DOM -> Value.DOM + HYBRID -> Value.HYBRID + CUA -> Value.CUA + 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 StagehandInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + DOM -> Known.DOM + HYBRID -> Known.HYBRID + CUA -> Known.CUA + else -> throw StagehandInvalidDataException("Unknown Mode: $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 StagehandInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + StagehandInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Mode = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Mode && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Model configuration object or model name string (e.g., 'openai/gpt-5-nano') */ + @JsonDeserialize(using = Model.Deserializer::class) + @JsonSerialize(using = Model.Serializer::class) + class Model + private constructor( + private val vertexModelConfigObject: VertexModelConfigObject? = null, + private val genericModelConfigObject: GenericModelConfigObject? = null, + private val string: String? = null, + private val _json: JsonValue? = null, + ) { + + fun vertexModelConfigObject(): Optional = + Optional.ofNullable(vertexModelConfigObject) + + fun genericModelConfigObject(): Optional = + Optional.ofNullable(genericModelConfigObject) + + fun string(): Optional = Optional.ofNullable(string) + + fun isVertexModelConfigObject(): Boolean = vertexModelConfigObject != null + + fun isGenericModelConfigObject(): Boolean = genericModelConfigObject != null + + fun isString(): Boolean = string != null + + fun asVertexModelConfigObject(): VertexModelConfigObject = + vertexModelConfigObject.getOrThrow("vertexModelConfigObject") + + fun asGenericModelConfigObject(): GenericModelConfigObject = + genericModelConfigObject.getOrThrow("genericModelConfigObject") + + fun asString(): String = string.getOrThrow("string") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given + * [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, + * unless [visitor] overrides [Visitor.unknown]. To handle variants not known to this + * version of the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.browserbase.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = model.accept(new Model.Visitor>() { + * @Override + * public Optional visitVertexModelConfigObject(VertexModelConfigObject vertexModelConfigObject) { + * return Optional.of(vertexModelConfigObject.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * * @throws StagehandInvalidDataException if [Visitor.unknown] is not overridden in * [visitor] and the current variant is unknown. */ fun accept(visitor: Visitor): T = when { - config != null -> visitor.visitConfig(config) + vertexModelConfigObject != null -> + visitor.visitVertexModelConfigObject(vertexModelConfigObject) + genericModelConfigObject != null -> + visitor.visitGenericModelConfigObject(genericModelConfigObject) string != null -> visitor.visitString(string) else -> visitor.unknown(_json) } - private var validated: Boolean = false + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Model = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ) { + vertexModelConfigObject.validate() + } + + override fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ) { + genericModelConfigObject.validate() + } + + override fun visitString(string: String) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ) = vertexModelConfigObject.validity() + + override fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ) = genericModelConfigObject.validity() + + override fun visitString(string: String) = 1 + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Model && + vertexModelConfigObject == other.vertexModelConfigObject && + genericModelConfigObject == other.genericModelConfigObject && + string == other.string + } + + override fun hashCode(): Int = + Objects.hash(vertexModelConfigObject, genericModelConfigObject, string) + + override fun toString(): String = + when { + vertexModelConfigObject != null -> + "Model{vertexModelConfigObject=$vertexModelConfigObject}" + genericModelConfigObject != null -> + "Model{genericModelConfigObject=$genericModelConfigObject}" + string != null -> "Model{string=$string}" + _json != null -> "Model{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Model") + } + + companion object { + + @JvmStatic + fun ofVertexModelConfigObject(vertexModelConfigObject: VertexModelConfigObject) = + Model(vertexModelConfigObject = vertexModelConfigObject) + + @JvmStatic + fun ofGenericModelConfigObject(genericModelConfigObject: GenericModelConfigObject) = + Model(genericModelConfigObject = genericModelConfigObject) + + @JvmStatic fun ofString(string: String) = Model(string = string) + } + + /** + * An interface that defines how to map each variant of [Model] to a value of type [T]. + */ + interface Visitor { + + fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ): T + + fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ): T + + fun visitString(string: String): T + + /** + * Maps an unknown variant of [Model] to a value of type [T]. + * + * An instance of [Model] 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 StagehandInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw StagehandInvalidDataException("Unknown Model: $json") + } + } + + internal class Deserializer : BaseDeserializer(Model::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Model { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { Model(vertexModelConfigObject = it, _json = json) }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { Model(genericModelConfigObject = it, _json = json) }, + tryDeserialize(node, jacksonTypeRef())?.let { + Model(string = 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 -> Model(_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(Model::class) { + + override fun serialize( + value: Model, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.vertexModelConfigObject != null -> + generator.writeObject(value.vertexModelConfigObject) + value.genericModelConfigObject != null -> + generator.writeObject(value.genericModelConfigObject) + value.string != null -> generator.writeObject(value.string) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Model") + } + } + } + + class VertexModelConfigObject + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val auth: JsonField, + private val modelName: JsonField, + private val provider: JsonValue, + private val providerOptions: JsonField, + private val apiKey: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("auth") @ExcludeMissing auth: JsonField = JsonMissing.of(), + @JsonProperty("modelName") + @ExcludeMissing + modelName: JsonField = JsonMissing.of(), + @JsonProperty("provider") + @ExcludeMissing + provider: JsonValue = JsonMissing.of(), + @JsonProperty("providerOptions") + @ExcludeMissing + providerOptions: JsonField = JsonMissing.of(), + @JsonProperty("apiKey") + @ExcludeMissing + apiKey: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + ) : this( + auth, + modelName, + provider, + providerOptions, + apiKey, + baseUrl, + headers, + mutableMapOf(), + ) + + /** + * Vertex provider authentication configuration + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun auth(): Auth = auth.getRequired("auth") + + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun modelName(): String = modelName.getRequired("modelName") + + /** + * Vertex AI model provider + * + * Expected to always return the following: + * ```java + * JsonValue.from("vertex") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonValue = provider + + /** + * Vertex provider-specific model configuration + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun providerOptions(): ProviderOptions = + providerOptions.getRequired("providerOptions") + + /** + * API key for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun apiKey(): Optional = apiKey.getOptional("apiKey") + + /** + * Base URL for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * Returns the raw JSON value of [auth]. + * + * Unlike [auth], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("auth") @ExcludeMissing fun _auth(): JsonField = auth + + /** + * Returns the raw JSON value of [modelName]. + * + * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("modelName") + @ExcludeMissing + fun _modelName(): JsonField = modelName + + /** + * Returns the raw JSON value of [providerOptions]. + * + * Unlike [providerOptions], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("providerOptions") + @ExcludeMissing + fun _providerOptions(): JsonField = providerOptions + + /** + * Returns the raw JSON value of [apiKey]. + * + * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + @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 + * [VertexModelConfigObject]. + * + * The following fields are required: + * ```java + * .auth() + * .modelName() + * .providerOptions() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [VertexModelConfigObject]. */ + class Builder internal constructor() { + + private var auth: JsonField? = null + private var modelName: JsonField? = null + private var provider: JsonValue = JsonValue.from("vertex") + private var providerOptions: JsonField? = null + private var apiKey: JsonField = JsonMissing.of() + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(vertexModelConfigObject: VertexModelConfigObject) = apply { + auth = vertexModelConfigObject.auth + modelName = vertexModelConfigObject.modelName + provider = vertexModelConfigObject.provider + providerOptions = vertexModelConfigObject.providerOptions + apiKey = vertexModelConfigObject.apiKey + baseUrl = vertexModelConfigObject.baseUrl + headers = vertexModelConfigObject.headers + additionalProperties = + vertexModelConfigObject.additionalProperties.toMutableMap() + } + + /** Vertex provider authentication configuration */ + fun auth(auth: Auth) = auth(JsonField.of(auth)) + + /** + * Sets [Builder.auth] to an arbitrary JSON value. + * + * You should usually call [Builder.auth] with a well-typed [Auth] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun auth(auth: JsonField) = apply { this.auth = auth } + + /** Model name string with provider prefix (e.g., 'openai/gpt-5-nano') */ + fun modelName(modelName: String) = modelName(JsonField.of(modelName)) + + /** + * Sets [Builder.modelName] to an arbitrary JSON value. + * + * You should usually call [Builder.modelName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun modelName(modelName: JsonField) = apply { + this.modelName = modelName + } + + /** + * 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("vertex") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun provider(provider: JsonValue) = apply { this.provider = provider } + + /** Vertex provider-specific model configuration */ + fun providerOptions(providerOptions: ProviderOptions) = + providerOptions(JsonField.of(providerOptions)) + + /** + * Sets [Builder.providerOptions] to an arbitrary JSON value. + * + * You should usually call [Builder.providerOptions] with a well-typed + * [ProviderOptions] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun providerOptions(providerOptions: JsonField) = apply { + this.providerOptions = providerOptions + } + + /** API key for the model provider */ + fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + + /** + * Sets [Builder.apiKey] to an arbitrary JSON value. + * + * You should usually call [Builder.apiKey] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } + + /** Base URL for the model provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl } + + /** Custom headers sent with every request to the model provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun headers(headers: JsonField) = apply { this.headers = headers } + + 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 [VertexModelConfigObject]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .auth() + * .modelName() + * .providerOptions() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): VertexModelConfigObject = + VertexModelConfigObject( + checkRequired("auth", auth), + checkRequired("modelName", modelName), + provider, + checkRequired("providerOptions", providerOptions), + apiKey, + baseUrl, + headers, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): VertexModelConfigObject = apply { + if (validated) { + return@apply + } + + auth().validate() + modelName() + _provider().let { + if (it != JsonValue.from("vertex")) { + throw StagehandInvalidDataException( + "'provider' is invalid, received $it" + ) + } + } + providerOptions().validate() + apiKey() + baseUrl() + headers().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = + (auth.asKnown().getOrNull()?.validity() ?: 0) + + (if (modelName.asKnown().isPresent) 1 else 0) + + provider.let { if (it == JsonValue.from("vertex")) 1 else 0 } + + (providerOptions.asKnown().getOrNull()?.validity() ?: 0) + + (if (apiKey.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + /** Vertex provider authentication configuration */ + class Auth + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val credentials: JsonField, + private val type: JsonValue, + private val projectId: JsonField, + private val scopes: JsonField, + private val universeDomain: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("credentials") + @ExcludeMissing + credentials: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("projectId") + @ExcludeMissing + projectId: JsonField = JsonMissing.of(), + @JsonProperty("scopes") + @ExcludeMissing + scopes: JsonField = JsonMissing.of(), + @JsonProperty("universeDomain") + @ExcludeMissing + universeDomain: JsonField = JsonMissing.of(), + ) : this(credentials, type, projectId, scopes, universeDomain, mutableMapOf()) + + /** + * Google Cloud service account credentials + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with + * an unexpected value). + */ + fun credentials(): Credentials = credentials.getRequired("credentials") + + /** + * Use inline Google Cloud service account credentials for provider + * authentication + * + * Expected to always return the following: + * ```java + * JsonValue.from("googleServiceAccount") + * ``` + * + * 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 + + /** + * Google Cloud project ID used by google-auth-library + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun projectId(): Optional = projectId.getOptional("projectId") + + /** + * Google auth scopes for the desired API request + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun scopes(): Optional = scopes.getOptional("scopes") + + /** + * Google Cloud universe domain + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun universeDomain(): Optional = + universeDomain.getOptional("universeDomain") + + /** + * Returns the raw JSON value of [credentials]. + * + * Unlike [credentials], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("credentials") + @ExcludeMissing + fun _credentials(): JsonField = credentials + + /** + * Returns the raw JSON value of [projectId]. + * + * Unlike [projectId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("projectId") + @ExcludeMissing + fun _projectId(): JsonField = projectId + + /** + * Returns the raw JSON value of [scopes]. + * + * Unlike [scopes], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("scopes") + @ExcludeMissing + fun _scopes(): JsonField = scopes + + /** + * Returns the raw JSON value of [universeDomain]. + * + * Unlike [universeDomain], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("universeDomain") + @ExcludeMissing + fun _universeDomain(): JsonField = universeDomain + + @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 [Auth]. + * + * The following fields are required: + * ```java + * .credentials() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Auth]. */ + class Builder internal constructor() { + + private var credentials: JsonField? = null + private var type: JsonValue = JsonValue.from("googleServiceAccount") + private var projectId: JsonField = JsonMissing.of() + private var scopes: JsonField = JsonMissing.of() + private var universeDomain: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(auth: Auth) = apply { + credentials = auth.credentials + type = auth.type + projectId = auth.projectId + scopes = auth.scopes + universeDomain = auth.universeDomain + additionalProperties = auth.additionalProperties.toMutableMap() + } + + /** Google Cloud service account credentials */ + fun credentials(credentials: Credentials) = + credentials(JsonField.of(credentials)) + + /** + * Sets [Builder.credentials] to an arbitrary JSON value. + * + * You should usually call [Builder.credentials] with a well-typed + * [Credentials] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun credentials(credentials: JsonField) = apply { + this.credentials = credentials + } + + /** + * 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("googleServiceAccount") + * ``` + * + * This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** Google Cloud project ID used by google-auth-library */ + fun projectId(projectId: String) = projectId(JsonField.of(projectId)) + + /** + * Sets [Builder.projectId] to an arbitrary JSON value. + * + * You should usually call [Builder.projectId] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun projectId(projectId: JsonField) = apply { + this.projectId = projectId + } + + /** Google auth scopes for the desired API request */ + fun scopes(scopes: Scopes) = scopes(JsonField.of(scopes)) + + /** + * Sets [Builder.scopes] to an arbitrary JSON value. + * + * You should usually call [Builder.scopes] with a well-typed [Scopes] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun scopes(scopes: JsonField) = apply { this.scopes = scopes } + + /** Alias for calling [scopes] with `Scopes.ofString(string)`. */ + fun scopes(string: String) = scopes(Scopes.ofString(string)) + + /** Alias for calling [scopes] with `Scopes.ofStrings(strings)`. */ + fun scopesOfStrings(strings: List) = + scopes(Scopes.ofStrings(strings)) + + /** Google Cloud universe domain */ + fun universeDomain(universeDomain: String) = + universeDomain(JsonField.of(universeDomain)) + + /** + * Sets [Builder.universeDomain] to an arbitrary JSON value. + * + * You should usually call [Builder.universeDomain] with a well-typed + * [String] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun universeDomain(universeDomain: JsonField) = apply { + this.universeDomain = universeDomain + } + + 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 [Auth]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .credentials() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Auth = + Auth( + checkRequired("credentials", credentials), + type, + projectId, + scopes, + universeDomain, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Auth = apply { + if (validated) { + return@apply + } + + credentials().validate() + _type().let { + if (it != JsonValue.from("googleServiceAccount")) { + throw StagehandInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + projectId() + scopes().ifPresent { it.validate() } + universeDomain() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = + (credentials.asKnown().getOrNull()?.validity() ?: 0) + + type.let { + if (it == JsonValue.from("googleServiceAccount")) 1 else 0 + } + + (if (projectId.asKnown().isPresent) 1 else 0) + + (scopes.asKnown().getOrNull()?.validity() ?: 0) + + (if (universeDomain.asKnown().isPresent) 1 else 0) + + /** Google Cloud service account credentials */ + class Credentials + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val clientEmail: JsonField, + private val privateKey: JsonField, + private val authProviderX509CertUrl: JsonField, + private val authUri: JsonField, + private val clientId: JsonField, + private val clientX509CertUrl: JsonField, + private val privateKeyId: JsonField, + private val projectId: JsonField, + private val tokenUri: JsonField, + private val type: JsonField, + private val universeDomain: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("client_email") + @ExcludeMissing + clientEmail: JsonField = JsonMissing.of(), + @JsonProperty("private_key") + @ExcludeMissing + privateKey: JsonField = JsonMissing.of(), + @JsonProperty("auth_provider_x509_cert_url") + @ExcludeMissing + authProviderX509CertUrl: JsonField = JsonMissing.of(), + @JsonProperty("auth_uri") + @ExcludeMissing + authUri: JsonField = JsonMissing.of(), + @JsonProperty("client_id") + @ExcludeMissing + clientId: JsonField = JsonMissing.of(), + @JsonProperty("client_x509_cert_url") + @ExcludeMissing + clientX509CertUrl: JsonField = JsonMissing.of(), + @JsonProperty("private_key_id") + @ExcludeMissing + privateKeyId: JsonField = JsonMissing.of(), + @JsonProperty("project_id") + @ExcludeMissing + projectId: JsonField = JsonMissing.of(), + @JsonProperty("token_uri") + @ExcludeMissing + tokenUri: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonField = JsonMissing.of(), + @JsonProperty("universe_domain") + @ExcludeMissing + universeDomain: JsonField = JsonMissing.of(), + ) : this( + clientEmail, + privateKey, + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + mutableMapOf(), + ) + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun clientEmail(): String = clientEmail.getRequired("client_email") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun privateKey(): String = privateKey.getRequired("private_key") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun authProviderX509CertUrl(): Optional = + authProviderX509CertUrl.getOptional("auth_provider_x509_cert_url") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun authUri(): Optional = authUri.getOptional("auth_uri") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun clientId(): Optional = clientId.getOptional("client_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun clientX509CertUrl(): Optional = + clientX509CertUrl.getOptional("client_x509_cert_url") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun privateKeyId(): Optional = + privateKeyId.getOptional("private_key_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun projectId(): Optional = projectId.getOptional("project_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun tokenUri(): Optional = tokenUri.getOptional("token_uri") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun universeDomain(): Optional = + universeDomain.getOptional("universe_domain") + + /** + * Returns the raw JSON value of [clientEmail]. + * + * Unlike [clientEmail], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("client_email") + @ExcludeMissing + fun _clientEmail(): JsonField = clientEmail + + /** + * Returns the raw JSON value of [privateKey]. + * + * Unlike [privateKey], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("private_key") + @ExcludeMissing + fun _privateKey(): JsonField = privateKey + + /** + * Returns the raw JSON value of [authProviderX509CertUrl]. + * + * Unlike [authProviderX509CertUrl], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("auth_provider_x509_cert_url") + @ExcludeMissing + fun _authProviderX509CertUrl(): JsonField = authProviderX509CertUrl + + /** + * Returns the raw JSON value of [authUri]. + * + * Unlike [authUri], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("auth_uri") + @ExcludeMissing + fun _authUri(): JsonField = authUri + + /** + * Returns the raw JSON value of [clientId]. + * + * Unlike [clientId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("client_id") + @ExcludeMissing + fun _clientId(): JsonField = clientId + + /** + * Returns the raw JSON value of [clientX509CertUrl]. + * + * Unlike [clientX509CertUrl], this method doesn't throw if the JSON field + * has an unexpected type. + */ + @JsonProperty("client_x509_cert_url") + @ExcludeMissing + fun _clientX509CertUrl(): JsonField = clientX509CertUrl + + /** + * Returns the raw JSON value of [privateKeyId]. + * + * Unlike [privateKeyId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("private_key_id") + @ExcludeMissing + fun _privateKeyId(): JsonField = privateKeyId + + /** + * Returns the raw JSON value of [projectId]. + * + * Unlike [projectId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("project_id") + @ExcludeMissing + fun _projectId(): JsonField = projectId + + /** + * Returns the raw JSON value of [tokenUri]. + * + * Unlike [tokenUri], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("token_uri") + @ExcludeMissing + fun _tokenUri(): JsonField = tokenUri + + /** + * 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 [universeDomain]. + * + * Unlike [universeDomain], this method doesn't throw if the JSON field has + * an unexpected type. + */ + @JsonProperty("universe_domain") + @ExcludeMissing + fun _universeDomain(): JsonField = universeDomain + + @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 + * [Credentials]. + * + * The following fields are required: + * ```java + * .clientEmail() + * .privateKey() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Credentials]. */ + class Builder internal constructor() { + + private var clientEmail: JsonField? = null + private var privateKey: JsonField? = null + private var authProviderX509CertUrl: JsonField = + JsonMissing.of() + private var authUri: JsonField = JsonMissing.of() + private var clientId: JsonField = JsonMissing.of() + private var clientX509CertUrl: JsonField = JsonMissing.of() + private var privateKeyId: JsonField = JsonMissing.of() + private var projectId: JsonField = JsonMissing.of() + private var tokenUri: JsonField = JsonMissing.of() + private var type: JsonField = JsonMissing.of() + private var universeDomain: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(credentials: Credentials) = apply { + clientEmail = credentials.clientEmail + privateKey = credentials.privateKey + authProviderX509CertUrl = credentials.authProviderX509CertUrl + authUri = credentials.authUri + clientId = credentials.clientId + clientX509CertUrl = credentials.clientX509CertUrl + privateKeyId = credentials.privateKeyId + projectId = credentials.projectId + tokenUri = credentials.tokenUri + type = credentials.type + universeDomain = credentials.universeDomain + additionalProperties = + credentials.additionalProperties.toMutableMap() + } + + fun clientEmail(clientEmail: String) = + clientEmail(JsonField.of(clientEmail)) + + /** + * Sets [Builder.clientEmail] to an arbitrary JSON value. + * + * You should usually call [Builder.clientEmail] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun clientEmail(clientEmail: JsonField) = apply { + this.clientEmail = clientEmail + } + + fun privateKey(privateKey: String) = + privateKey(JsonField.of(privateKey)) + + /** + * Sets [Builder.privateKey] to an arbitrary JSON value. + * + * You should usually call [Builder.privateKey] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun privateKey(privateKey: JsonField) = apply { + this.privateKey = privateKey + } + + fun authProviderX509CertUrl(authProviderX509CertUrl: String) = + authProviderX509CertUrl(JsonField.of(authProviderX509CertUrl)) + + /** + * Sets [Builder.authProviderX509CertUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.authProviderX509CertUrl] with a + * well-typed [String] value instead. This method is primarily for + * setting the field to an undocumented or not yet supported value. + */ + fun authProviderX509CertUrl( + authProviderX509CertUrl: JsonField + ) = apply { this.authProviderX509CertUrl = authProviderX509CertUrl } + + fun authUri(authUri: String) = authUri(JsonField.of(authUri)) + + /** + * Sets [Builder.authUri] to an arbitrary JSON value. + * + * You should usually call [Builder.authUri] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun authUri(authUri: JsonField) = apply { + this.authUri = authUri + } + + fun clientId(clientId: String) = clientId(JsonField.of(clientId)) + + /** + * Sets [Builder.clientId] to an arbitrary JSON value. + * + * You should usually call [Builder.clientId] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun clientId(clientId: JsonField) = apply { + this.clientId = clientId + } + + fun clientX509CertUrl(clientX509CertUrl: String) = + clientX509CertUrl(JsonField.of(clientX509CertUrl)) + + /** + * Sets [Builder.clientX509CertUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.clientX509CertUrl] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun clientX509CertUrl(clientX509CertUrl: JsonField) = apply { + this.clientX509CertUrl = clientX509CertUrl + } + + fun privateKeyId(privateKeyId: String) = + privateKeyId(JsonField.of(privateKeyId)) + + /** + * Sets [Builder.privateKeyId] to an arbitrary JSON value. + * + * You should usually call [Builder.privateKeyId] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun privateKeyId(privateKeyId: JsonField) = apply { + this.privateKeyId = privateKeyId + } + + fun projectId(projectId: String) = projectId(JsonField.of(projectId)) + + /** + * Sets [Builder.projectId] to an arbitrary JSON value. + * + * You should usually call [Builder.projectId] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun projectId(projectId: JsonField) = apply { + this.projectId = projectId + } + + fun tokenUri(tokenUri: String) = tokenUri(JsonField.of(tokenUri)) + + /** + * Sets [Builder.tokenUri] to an arbitrary JSON value. + * + * You should usually call [Builder.tokenUri] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun tokenUri(tokenUri: JsonField) = apply { + this.tokenUri = tokenUri + } + + 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 universeDomain(universeDomain: String) = + universeDomain(JsonField.of(universeDomain)) + + /** + * Sets [Builder.universeDomain] to an arbitrary JSON value. + * + * You should usually call [Builder.universeDomain] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun universeDomain(universeDomain: JsonField) = apply { + this.universeDomain = universeDomain + } + + 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 [Credentials]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + * + * The following fields are required: + * ```java + * .clientEmail() + * .privateKey() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Credentials = + Credentials( + checkRequired("clientEmail", clientEmail), + checkRequired("privateKey", privateKey), + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Credentials = apply { + if (validated) { + return@apply + } + + clientEmail() + privateKey() + authProviderX509CertUrl() + authUri() + clientId() + clientX509CertUrl() + privateKeyId() + projectId() + tokenUri() + type().ifPresent { it.validate() } + universeDomain() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (clientEmail.asKnown().isPresent) 1 else 0) + + (if (privateKey.asKnown().isPresent) 1 else 0) + + (if (authProviderX509CertUrl.asKnown().isPresent) 1 else 0) + + (if (authUri.asKnown().isPresent) 1 else 0) + + (if (clientId.asKnown().isPresent) 1 else 0) + + (if (clientX509CertUrl.asKnown().isPresent) 1 else 0) + + (if (privateKeyId.asKnown().isPresent) 1 else 0) + + (if (projectId.asKnown().isPresent) 1 else 0) + + (if (tokenUri.asKnown().isPresent) 1 else 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + (if (universeDomain.asKnown().isPresent) 1 else 0) + + 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 SERVICE_ACCOUNT = of("service_account") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + SERVICE_ACCOUNT + } + + /** + * 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 { + SERVICE_ACCOUNT, + /** + * 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) { + SERVICE_ACCOUNT -> Value.SERVICE_ACCOUNT + 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 StagehandInvalidDataException if this class instance's value + * is a not a known member. + */ + fun known(): Known = + when (this) { + SERVICE_ACCOUNT -> Known.SERVICE_ACCOUNT + else -> + throw StagehandInvalidDataException("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 StagehandInvalidDataException if this class instance's value + * does not have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + StagehandInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API + * for existing fields. + * + * @throws StagehandInvalidDataException if any value type in this + * object doesn't match its expected type. + */ + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Type && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Credentials && + clientEmail == other.clientEmail && + privateKey == other.privateKey && + authProviderX509CertUrl == other.authProviderX509CertUrl && + authUri == other.authUri && + clientId == other.clientId && + clientX509CertUrl == other.clientX509CertUrl && + privateKeyId == other.privateKeyId && + projectId == other.projectId && + tokenUri == other.tokenUri && + type == other.type && + universeDomain == other.universeDomain && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + clientEmail, + privateKey, + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Credentials{clientEmail=$clientEmail, privateKey=$privateKey, authProviderX509CertUrl=$authProviderX509CertUrl, authUri=$authUri, clientId=$clientId, clientX509CertUrl=$clientX509CertUrl, privateKeyId=$privateKeyId, projectId=$projectId, tokenUri=$tokenUri, type=$type, universeDomain=$universeDomain, additionalProperties=$additionalProperties}" + } + + /** Google auth scopes for the desired API request */ + @JsonDeserialize(using = Scopes.Deserializer::class) + @JsonSerialize(using = Scopes.Serializer::class) + class Scopes + private constructor( + private val string: String? = null, + private val strings: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun strings(): Optional> = Optional.ofNullable(strings) + + fun isString(): Boolean = string != null + + fun isStrings(): Boolean = strings != null + + fun asString(): String = string.getOrThrow("string") + + fun asStrings(): List = strings.getOrThrow("strings") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the + * given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from + * the API, unless [visitor] overrides [Visitor.unknown]. To handle variants + * not known to this version of the SDK gracefully, consider overriding + * [Visitor.unknown]: + * ```java + * import com.browserbase.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = scopes.accept(new Scopes.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws StagehandInvalidDataException if [Visitor.unknown] is not + * overridden in [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + strings != null -> visitor.visitStrings(strings) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Scopes = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitStrings(strings: List) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 visitStrings(strings: List) = strings.size + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Scopes && + string == other.string && + strings == other.strings + } + + override fun hashCode(): Int = Objects.hash(string, strings) + + override fun toString(): String = + when { + string != null -> "Scopes{string=$string}" + strings != null -> "Scopes{strings=$strings}" + _json != null -> "Scopes{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Scopes") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Scopes(string = string) + + @JvmStatic + fun ofStrings(strings: List) = + Scopes(strings = strings.toImmutable()) + } + + /** + * An interface that defines how to map each variant of [Scopes] to a value + * of type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitStrings(strings: List): T + + /** + * Maps an unknown variant of [Scopes] to a value of type [T]. + * + * An instance of [Scopes] 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 StagehandInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw StagehandInvalidDataException("Unknown Scopes: $json") + } + } + + internal class Deserializer : BaseDeserializer(Scopes::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Scopes { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Scopes(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>()) + ?.let { Scopes(strings = 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 -> Scopes(_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(Scopes::class) { + + override fun serialize( + value: Scopes, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.strings != null -> generator.writeObject(value.strings) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Scopes") + } + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Auth && + credentials == other.credentials && + type == other.type && + projectId == other.projectId && + scopes == other.scopes && + universeDomain == other.universeDomain && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + credentials, + type, + projectId, + scopes, + universeDomain, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Auth{credentials=$credentials, type=$type, projectId=$projectId, scopes=$scopes, universeDomain=$universeDomain, additionalProperties=$additionalProperties}" + } + + /** Vertex provider-specific model configuration */ + class ProviderOptions + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val vertex: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("vertex") + @ExcludeMissing + vertex: JsonField = JsonMissing.of() + ) : this(vertex, mutableMapOf()) + + /** + * Vertex AI provider-specific settings + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with + * an unexpected value). + */ + fun vertex(): Vertex = vertex.getRequired("vertex") + + /** + * Returns the raw JSON value of [vertex]. + * + * Unlike [vertex], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("vertex") + @ExcludeMissing + fun _vertex(): JsonField = vertex + + @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 + * [ProviderOptions]. + * + * The following fields are required: + * ```java + * .vertex() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ProviderOptions]. */ + class Builder internal constructor() { + + private var vertex: JsonField? = null + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(providerOptions: ProviderOptions) = apply { + vertex = providerOptions.vertex + additionalProperties = + providerOptions.additionalProperties.toMutableMap() + } + + /** Vertex AI provider-specific settings */ + fun vertex(vertex: Vertex) = vertex(JsonField.of(vertex)) + + /** + * Sets [Builder.vertex] to an arbitrary JSON value. + * + * You should usually call [Builder.vertex] with a well-typed [Vertex] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun vertex(vertex: JsonField) = apply { this.vertex = vertex } + + 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 [ProviderOptions]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .vertex() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ProviderOptions = + ProviderOptions( + checkRequired("vertex", vertex), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): ProviderOptions = apply { + if (validated) { + return@apply + } + + vertex().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = (vertex.asKnown().getOrNull()?.validity() ?: 0) + + /** Vertex AI provider-specific settings */ + class Vertex + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val location: JsonField, + private val project: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("location") + @ExcludeMissing + location: JsonField = JsonMissing.of(), + @JsonProperty("project") + @ExcludeMissing + project: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + ) : this(location, project, baseUrl, headers, mutableMapOf()) + + /** + * Google Cloud location for Vertex AI models + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun location(): String = location.getRequired("location") + + /** + * Google Cloud project ID for Vertex AI models + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun project(): String = project.getRequired("project") + + /** + * Base URL for the Vertex AI provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the Vertex AI provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * Returns the raw JSON value of [location]. + * + * Unlike [location], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("location") + @ExcludeMissing + fun _location(): JsonField = location + + /** + * Returns the raw JSON value of [project]. + * + * Unlike [project], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("project") + @ExcludeMissing + fun _project(): JsonField = project + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("baseURL") + @ExcludeMissing + fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + @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 [Vertex]. + * + * The following fields are required: + * ```java + * .location() + * .project() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Vertex]. */ + class Builder internal constructor() { + + private var location: JsonField? = null + private var project: JsonField? = null + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(vertex: Vertex) = apply { + location = vertex.location + project = vertex.project + baseUrl = vertex.baseUrl + headers = vertex.headers + additionalProperties = vertex.additionalProperties.toMutableMap() + } + + /** Google Cloud location for Vertex AI models */ + fun location(location: String) = location(JsonField.of(location)) + + /** + * Sets [Builder.location] to an arbitrary JSON value. + * + * You should usually call [Builder.location] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun location(location: JsonField) = apply { + this.location = location + } + + /** Google Cloud project ID for Vertex AI models */ + fun project(project: String) = project(JsonField.of(project)) + + /** + * Sets [Builder.project] to an arbitrary JSON value. + * + * You should usually call [Builder.project] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun project(project: JsonField) = apply { + this.project = project + } + + /** Base URL for the Vertex AI provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { + this.baseUrl = baseUrl + } + + /** Custom headers sent with every request to the Vertex AI provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun headers(headers: JsonField) = apply { + this.headers = headers + } + + 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 [Vertex]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + * + * The following fields are required: + * ```java + * .location() + * .project() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Vertex = + Vertex( + checkRequired("location", location), + checkRequired("project", project), + baseUrl, + headers, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Vertex = apply { + if (validated) { + return@apply + } + + location() + project() + baseUrl() + headers().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (location.asKnown().isPresent) 1 else 0) + + (if (project.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + /** Custom headers sent with every request to the Vertex AI provider */ + class Headers + @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 + * [Headers]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = + headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API + * for existing fields. + * + * @throws StagehandInvalidDataException if any value type in this + * object doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Headers{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Vertex && + location == other.location && + project == other.project && + baseUrl == other.baseUrl && + headers == other.headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(location, project, baseUrl, headers, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Vertex{location=$location, project=$project, baseUrl=$baseUrl, headers=$headers, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ProviderOptions && + vertex == other.vertex && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(vertex, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ProviderOptions{vertex=$vertex, additionalProperties=$additionalProperties}" + } + + /** Custom headers sent with every request to the model provider */ + class Headers + @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 [Headers]. */ + @JvmStatic fun builder() = Builder() + } - /** - * Validates that the types of all values in this object match their expected types - * recursively. - * - * This method is _not_ forwards compatible with new types from the API for existing - * fields. - * - * @throws StagehandInvalidDataException if any value type in this object doesn't match - * its expected type. - */ - fun validate(): Model = apply { - if (validated) { - return@apply - } + /** A builder for [Headers]. */ + class Builder internal constructor() { - accept( - object : Visitor { - override fun visitConfig(config: ModelConfig) { - config.validate() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.additionalProperties.toMutableMap() } - override fun visitString(string: String) {} + 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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) } - ) - validated = true - } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: StagehandInvalidDataException) { - false - } + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } - /** - * 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 visitConfig(config: ModelConfig) = config.validity() + validated = true + } - override fun visitString(string: String) = 1 + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + false + } - override fun unknown(json: JsonValue?) = 0 + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + additionalProperties.count { (_, value) -> + !value.isNull() && !value.isMissing() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Headers && + additionalProperties == other.additionalProperties } - ) - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } - return other is Model && config == other.config && string == other.string - } + override fun hashCode(): Int = hashCode + + override fun toString() = "Headers{additionalProperties=$additionalProperties}" + } - override fun hashCode(): Int = Objects.hash(config, string) + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - override fun toString(): String = - when { - config != null -> "Model{config=$config}" - string != null -> "Model{string=$string}" - _json != null -> "Model{_unknown=$_json}" - else -> throw IllegalStateException("Invalid Model") + return other is VertexModelConfigObject && + auth == other.auth && + modelName == other.modelName && + provider == other.provider && + providerOptions == other.providerOptions && + apiKey == other.apiKey && + baseUrl == other.baseUrl && + headers == other.headers && + additionalProperties == other.additionalProperties } - companion object { + private val hashCode: Int by lazy { + Objects.hash( + auth, + modelName, + provider, + providerOptions, + apiKey, + baseUrl, + headers, + additionalProperties, + ) + } - @JvmStatic fun ofConfig(config: ModelConfig) = Model(config = config) + override fun hashCode(): Int = hashCode - @JvmStatic fun ofString(string: String) = Model(string = string) + override fun toString() = + "VertexModelConfigObject{auth=$auth, modelName=$modelName, provider=$provider, providerOptions=$providerOptions, apiKey=$apiKey, baseUrl=$baseUrl, headers=$headers, additionalProperties=$additionalProperties}" } - /** - * An interface that defines how to map each variant of [Model] to a value of type [T]. - */ - interface Visitor { + class GenericModelConfigObject + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val modelName: JsonField, + private val apiKey: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val provider: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("modelName") + @ExcludeMissing + modelName: JsonField = JsonMissing.of(), + @JsonProperty("apiKey") + @ExcludeMissing + apiKey: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + @JsonProperty("provider") + @ExcludeMissing + provider: JsonField = JsonMissing.of(), + ) : this(modelName, apiKey, baseUrl, headers, provider, mutableMapOf()) - fun visitConfig(config: ModelConfig): T + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun modelName(): String = modelName.getRequired("modelName") - fun visitString(string: String): T + /** + * API key for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun apiKey(): Optional = apiKey.getOptional("apiKey") /** - * Maps an unknown variant of [Model] to a value of type [T]. + * Base URL for the model provider * - * An instance of [Model] 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 StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the model provider * - * @throws StagehandInvalidDataException in the default implementation. + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). */ - fun unknown(json: JsonValue?): T { - throw StagehandInvalidDataException("Unknown Model: $json") + fun headers(): Optional = headers.getOptional("headers") + + /** + * AI provider for the model (or provide a baseURL endpoint instead) + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun provider(): Optional = provider.getOptional("provider") + + /** + * Returns the raw JSON value of [modelName]. + * + * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("modelName") + @ExcludeMissing + fun _modelName(): JsonField = modelName + + /** + * Returns the raw JSON value of [apiKey]. + * + * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + /** + * Returns the raw JSON value of [provider]. + * + * Unlike [provider], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("provider") + @ExcludeMissing + fun _provider(): JsonField = provider + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) } - } - internal class Deserializer : BaseDeserializer(Model::class) { + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [GenericModelConfigObject]. + * + * The following fields are required: + * ```java + * .modelName() + * ``` + */ + @JvmStatic fun builder() = Builder() + } - override fun ObjectCodec.deserialize(node: JsonNode): Model { - val json = JsonValue.fromJsonNode(node) + /** A builder for [GenericModelConfigObject]. */ + class Builder internal constructor() { + + private var modelName: JsonField? = null + private var apiKey: JsonField = JsonMissing.of() + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var provider: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(genericModelConfigObject: GenericModelConfigObject) = apply { + modelName = genericModelConfigObject.modelName + apiKey = genericModelConfigObject.apiKey + baseUrl = genericModelConfigObject.baseUrl + headers = genericModelConfigObject.headers + provider = genericModelConfigObject.provider + additionalProperties = + genericModelConfigObject.additionalProperties.toMutableMap() + } - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef())?.let { - Model(config = it, _json = json) - }, - tryDeserialize(node, jacksonTypeRef())?.let { - Model(string = 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 -> Model(_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() + /** Model name string with provider prefix (e.g., 'openai/gpt-5-nano') */ + fun modelName(modelName: String) = modelName(JsonField.of(modelName)) + + /** + * Sets [Builder.modelName] to an arbitrary JSON value. + * + * You should usually call [Builder.modelName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun modelName(modelName: JsonField) = apply { + this.modelName = modelName + } + + /** API key for the model provider */ + fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + + /** + * Sets [Builder.apiKey] to an arbitrary JSON value. + * + * You should usually call [Builder.apiKey] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } + + /** Base URL for the model provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl } + + /** Custom headers sent with every request to the model provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun headers(headers: JsonField) = apply { this.headers = headers } + + /** AI provider for the model (or provide a baseURL endpoint instead) */ + fun provider(provider: Provider) = provider(JsonField.of(provider)) + + /** + * Sets [Builder.provider] to an arbitrary JSON value. + * + * You should usually call [Builder.provider] with a well-typed [Provider] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun provider(provider: JsonField) = apply { this.provider = provider } + + 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 [GenericModelConfigObject]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .modelName() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): GenericModelConfigObject = + GenericModelConfigObject( + checkRequired("modelName", modelName), + apiKey, + baseUrl, + headers, + provider, + additionalProperties.toMutableMap(), + ) } - } - internal class Serializer : BaseSerializer(Model::class) { + private var validated: Boolean = false - override fun serialize( - value: Model, - generator: JsonGenerator, - provider: SerializerProvider, + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): GenericModelConfigObject = apply { + if (validated) { + return@apply + } + + modelName() + apiKey() + baseUrl() + headers().ifPresent { it.validate() } + provider().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (modelName.asKnown().isPresent) 1 else 0) + + (if (apiKey.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + (provider.asKnown().getOrNull()?.validity() ?: 0) + + /** Custom headers sent with every request to the model provider */ + class Headers + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map ) { - when { - value.config != null -> generator.writeObject(value.config) - value.string != null -> generator.writeObject(value.string) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid Model") + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Headers]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Headers{additionalProperties=$additionalProperties}" + } + + /** AI provider for the model (or provide a baseURL endpoint instead) */ + class Provider + @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 OPENAI = of("openai") + + @JvmField val ANTHROPIC = of("anthropic") + + @JvmField val GOOGLE = of("google") + + @JvmField val MICROSOFT = of("microsoft") + + @JvmField val BEDROCK = of("bedrock") + + @JvmStatic fun of(value: String) = Provider(JsonField.of(value)) + } + + /** An enum containing [Provider]'s known values. */ + enum class Known { + OPENAI, + ANTHROPIC, + GOOGLE, + MICROSOFT, + BEDROCK, + } + + /** + * An enum containing [Provider]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [Provider] 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 { + OPENAI, + ANTHROPIC, + GOOGLE, + MICROSOFT, + BEDROCK, + /** + * An enum member indicating that [Provider] 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) { + OPENAI -> Value.OPENAI + ANTHROPIC -> Value.ANTHROPIC + GOOGLE -> Value.GOOGLE + MICROSOFT -> Value.MICROSOFT + BEDROCK -> Value.BEDROCK + 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 StagehandInvalidDataException if this class instance's value is a not + * a known member. + */ + fun known(): Known = + when (this) { + OPENAI -> Known.OPENAI + ANTHROPIC -> Known.ANTHROPIC + GOOGLE -> Known.GOOGLE + MICROSOFT -> Known.MICROSOFT + BEDROCK -> Known.BEDROCK + else -> throw StagehandInvalidDataException("Unknown Provider: $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 StagehandInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + StagehandInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Provider = apply { + if (validated) { + return@apply + } + + known() + validated = true } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Provider && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is GenericModelConfigObject && + modelName == other.modelName && + apiKey == other.apiKey && + baseUrl == other.baseUrl && + headers == other.headers && + provider == other.provider && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + modelName, + apiKey, + baseUrl, + headers, + provider, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "GenericModelConfigObject{modelName=$modelName, apiKey=$apiKey, baseUrl=$baseUrl, headers=$headers, provider=$provider, additionalProperties=$additionalProperties}" } } diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractParams.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractParams.kt index 5c5fe92..2dea515 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractParams.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractParams.kt @@ -12,6 +12,7 @@ import com.browserbase.api.core.JsonValue import com.browserbase.api.core.Params import com.browserbase.api.core.allMaxBy import com.browserbase.api.core.checkKnown +import com.browserbase.api.core.checkRequired import com.browserbase.api.core.getOrThrow import com.browserbase.api.core.http.Headers import com.browserbase.api.core.http.QueryParams @@ -635,6 +636,7 @@ private constructor( private constructor( private val ignoreSelectors: JsonField>, private val model: JsonField, + private val screenshot: JsonField, private val selector: JsonField, private val timeout: JsonField, private val additionalProperties: MutableMap, @@ -646,11 +648,14 @@ private constructor( @ExcludeMissing ignoreSelectors: JsonField> = JsonMissing.of(), @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + @JsonProperty("screenshot") + @ExcludeMissing + screenshot: JsonField = JsonMissing.of(), @JsonProperty("selector") @ExcludeMissing selector: JsonField = JsonMissing.of(), @JsonProperty("timeout") @ExcludeMissing timeout: JsonField = JsonMissing.of(), - ) : this(ignoreSelectors, model, selector, timeout, mutableMapOf()) + ) : this(ignoreSelectors, model, screenshot, selector, timeout, mutableMapOf()) /** * Selectors for elements and subtrees that should be excluded from extraction @@ -669,6 +674,15 @@ private constructor( */ fun model(): Optional = model.getOptional("model") + /** + * When true, include a screenshot of the current viewport in the extraction LLM call. + * Defaults to false. + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun screenshot(): Optional = screenshot.getOptional("screenshot") + /** * CSS selector to scope extraction to a specific element * @@ -702,6 +716,15 @@ private constructor( */ @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + /** + * Returns the raw JSON value of [screenshot]. + * + * Unlike [screenshot], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("screenshot") + @ExcludeMissing + fun _screenshot(): JsonField = screenshot + /** * Returns the raw JSON value of [selector]. * @@ -739,6 +762,7 @@ private constructor( private var ignoreSelectors: JsonField>? = null private var model: JsonField = JsonMissing.of() + private var screenshot: JsonField = JsonMissing.of() private var selector: JsonField = JsonMissing.of() private var timeout: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -747,6 +771,7 @@ private constructor( internal fun from(options: Options) = apply { ignoreSelectors = options.ignoreSelectors.map { it.toMutableList() } model = options.model + screenshot = options.screenshot selector = options.selector timeout = options.timeout additionalProperties = options.additionalProperties.toMutableMap() @@ -791,12 +816,38 @@ private constructor( */ fun model(model: JsonField) = apply { this.model = model } - /** Alias for calling [model] with `Model.ofConfig(config)`. */ - fun model(config: ModelConfig) = model(Model.ofConfig(config)) + /** + * Alias for calling [model] with + * `Model.ofVertexModelConfigObject(vertexModelConfigObject)`. + */ + fun model(vertexModelConfigObject: Model.VertexModelConfigObject) = + model(Model.ofVertexModelConfigObject(vertexModelConfigObject)) + + /** + * Alias for calling [model] with + * `Model.ofGenericModelConfigObject(genericModelConfigObject)`. + */ + fun model(genericModelConfigObject: Model.GenericModelConfigObject) = + model(Model.ofGenericModelConfigObject(genericModelConfigObject)) /** Alias for calling [model] with `Model.ofString(string)`. */ fun model(string: String) = model(Model.ofString(string)) + /** + * When true, include a screenshot of the current viewport in the extraction LLM call. + * Defaults to false. + */ + fun screenshot(screenshot: Boolean) = screenshot(JsonField.of(screenshot)) + + /** + * Sets [Builder.screenshot] to an arbitrary JSON value. + * + * You should usually call [Builder.screenshot] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun screenshot(screenshot: JsonField) = apply { this.screenshot = screenshot } + /** CSS selector to scope extraction to a specific element */ fun selector(selector: String) = selector(JsonField.of(selector)) @@ -849,6 +900,7 @@ private constructor( Options( (ignoreSelectors ?: JsonMissing.of()).map { it.toImmutable() }, model, + screenshot, selector, timeout, additionalProperties.toMutableMap(), @@ -873,6 +925,7 @@ private constructor( ignoreSelectors() model().ifPresent { it.validate() } + screenshot() selector() timeout() validated = true @@ -896,6 +949,7 @@ private constructor( internal fun validity(): Int = (ignoreSelectors.asKnown().getOrNull()?.size ?: 0) + (model.asKnown().getOrNull()?.validity() ?: 0) + + (if (screenshot.asKnown().isPresent) 1 else 0) + (if (selector.asKnown().isPresent) 1 else 0) + (if (timeout.asKnown().isPresent) 1 else 0) @@ -904,20 +958,31 @@ private constructor( @JsonSerialize(using = Model.Serializer::class) class Model private constructor( - private val config: ModelConfig? = null, + private val vertexModelConfigObject: VertexModelConfigObject? = null, + private val genericModelConfigObject: GenericModelConfigObject? = null, private val string: String? = null, private val _json: JsonValue? = null, ) { - fun config(): Optional = Optional.ofNullable(config) + fun vertexModelConfigObject(): Optional = + Optional.ofNullable(vertexModelConfigObject) + + fun genericModelConfigObject(): Optional = + Optional.ofNullable(genericModelConfigObject) fun string(): Optional = Optional.ofNullable(string) - fun isConfig(): Boolean = config != null + fun isVertexModelConfigObject(): Boolean = vertexModelConfigObject != null + + fun isGenericModelConfigObject(): Boolean = genericModelConfigObject != null fun isString(): Boolean = string != null - fun asConfig(): ModelConfig = config.getOrThrow("config") + fun asVertexModelConfigObject(): VertexModelConfigObject = + vertexModelConfigObject.getOrThrow("vertexModelConfigObject") + + fun asGenericModelConfigObject(): GenericModelConfigObject = + genericModelConfigObject.getOrThrow("genericModelConfigObject") fun asString(): String = string.getOrThrow("string") @@ -936,8 +1001,8 @@ private constructor( * * Optional result = model.accept(new Model.Visitor>() { * @Override - * public Optional visitConfig(ModelConfig config) { - * return Optional.of(config.toString()); + * public Optional visitVertexModelConfigObject(VertexModelConfigObject vertexModelConfigObject) { + * return Optional.of(vertexModelConfigObject.toString()); * } * * // ... @@ -955,7 +1020,10 @@ private constructor( */ fun accept(visitor: Visitor): T = when { - config != null -> visitor.visitConfig(config) + vertexModelConfigObject != null -> + visitor.visitVertexModelConfigObject(vertexModelConfigObject) + genericModelConfigObject != null -> + visitor.visitGenericModelConfigObject(genericModelConfigObject) string != null -> visitor.visitString(string) else -> visitor.unknown(_json) } @@ -979,8 +1047,16 @@ private constructor( accept( object : Visitor { - override fun visitConfig(config: ModelConfig) { - config.validate() + override fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ) { + vertexModelConfigObject.validate() + } + + override fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ) { + genericModelConfigObject.validate() } override fun visitString(string: String) {} @@ -1007,7 +1083,13 @@ private constructor( internal fun validity(): Int = accept( object : Visitor { - override fun visitConfig(config: ModelConfig) = config.validity() + override fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ) = vertexModelConfigObject.validity() + + override fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ) = genericModelConfigObject.validity() override fun visitString(string: String) = 1 @@ -1020,14 +1102,21 @@ private constructor( return true } - return other is Model && config == other.config && string == other.string + return other is Model && + vertexModelConfigObject == other.vertexModelConfigObject && + genericModelConfigObject == other.genericModelConfigObject && + string == other.string } - override fun hashCode(): Int = Objects.hash(config, string) + override fun hashCode(): Int = + Objects.hash(vertexModelConfigObject, genericModelConfigObject, string) override fun toString(): String = when { - config != null -> "Model{config=$config}" + vertexModelConfigObject != null -> + "Model{vertexModelConfigObject=$vertexModelConfigObject}" + genericModelConfigObject != null -> + "Model{genericModelConfigObject=$genericModelConfigObject}" string != null -> "Model{string=$string}" _json != null -> "Model{_unknown=$_json}" else -> throw IllegalStateException("Invalid Model") @@ -1035,7 +1124,13 @@ private constructor( companion object { - @JvmStatic fun ofConfig(config: ModelConfig) = Model(config = config) + @JvmStatic + fun ofVertexModelConfigObject(vertexModelConfigObject: VertexModelConfigObject) = + Model(vertexModelConfigObject = vertexModelConfigObject) + + @JvmStatic + fun ofGenericModelConfigObject(genericModelConfigObject: GenericModelConfigObject) = + Model(genericModelConfigObject = genericModelConfigObject) @JvmStatic fun ofString(string: String) = Model(string = string) } @@ -1045,7 +1140,13 @@ private constructor( */ interface Visitor { - fun visitConfig(config: ModelConfig): T + fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ): T + + fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ): T fun visitString(string: String): T @@ -1071,9 +1172,10 @@ private constructor( val bestMatches = sequenceOf( - tryDeserialize(node, jacksonTypeRef())?.let { - Model(config = it, _json = json) - }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { Model(vertexModelConfigObject = it, _json = json) }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { Model(genericModelConfigObject = it, _json = json) }, tryDeserialize(node, jacksonTypeRef())?.let { Model(string = it, _json = json) }, @@ -1102,13 +1204,3179 @@ private constructor( provider: SerializerProvider, ) { when { - value.config != null -> generator.writeObject(value.config) + value.vertexModelConfigObject != null -> + generator.writeObject(value.vertexModelConfigObject) + value.genericModelConfigObject != null -> + generator.writeObject(value.genericModelConfigObject) value.string != null -> generator.writeObject(value.string) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid Model") } } } + + class VertexModelConfigObject + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val auth: JsonField, + private val modelName: JsonField, + private val provider: JsonValue, + private val providerOptions: JsonField, + private val apiKey: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("auth") @ExcludeMissing auth: JsonField = JsonMissing.of(), + @JsonProperty("modelName") + @ExcludeMissing + modelName: JsonField = JsonMissing.of(), + @JsonProperty("provider") + @ExcludeMissing + provider: JsonValue = JsonMissing.of(), + @JsonProperty("providerOptions") + @ExcludeMissing + providerOptions: JsonField = JsonMissing.of(), + @JsonProperty("apiKey") + @ExcludeMissing + apiKey: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + ) : this( + auth, + modelName, + provider, + providerOptions, + apiKey, + baseUrl, + headers, + mutableMapOf(), + ) + + /** + * Vertex provider authentication configuration + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun auth(): Auth = auth.getRequired("auth") + + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun modelName(): String = modelName.getRequired("modelName") + + /** + * Vertex AI model provider + * + * Expected to always return the following: + * ```java + * JsonValue.from("vertex") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonValue = provider + + /** + * Vertex provider-specific model configuration + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun providerOptions(): ProviderOptions = + providerOptions.getRequired("providerOptions") + + /** + * API key for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun apiKey(): Optional = apiKey.getOptional("apiKey") + + /** + * Base URL for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * Returns the raw JSON value of [auth]. + * + * Unlike [auth], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("auth") @ExcludeMissing fun _auth(): JsonField = auth + + /** + * Returns the raw JSON value of [modelName]. + * + * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("modelName") + @ExcludeMissing + fun _modelName(): JsonField = modelName + + /** + * Returns the raw JSON value of [providerOptions]. + * + * Unlike [providerOptions], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("providerOptions") + @ExcludeMissing + fun _providerOptions(): JsonField = providerOptions + + /** + * Returns the raw JSON value of [apiKey]. + * + * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + @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 + * [VertexModelConfigObject]. + * + * The following fields are required: + * ```java + * .auth() + * .modelName() + * .providerOptions() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [VertexModelConfigObject]. */ + class Builder internal constructor() { + + private var auth: JsonField? = null + private var modelName: JsonField? = null + private var provider: JsonValue = JsonValue.from("vertex") + private var providerOptions: JsonField? = null + private var apiKey: JsonField = JsonMissing.of() + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(vertexModelConfigObject: VertexModelConfigObject) = apply { + auth = vertexModelConfigObject.auth + modelName = vertexModelConfigObject.modelName + provider = vertexModelConfigObject.provider + providerOptions = vertexModelConfigObject.providerOptions + apiKey = vertexModelConfigObject.apiKey + baseUrl = vertexModelConfigObject.baseUrl + headers = vertexModelConfigObject.headers + additionalProperties = + vertexModelConfigObject.additionalProperties.toMutableMap() + } + + /** Vertex provider authentication configuration */ + fun auth(auth: Auth) = auth(JsonField.of(auth)) + + /** + * Sets [Builder.auth] to an arbitrary JSON value. + * + * You should usually call [Builder.auth] with a well-typed [Auth] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun auth(auth: JsonField) = apply { this.auth = auth } + + /** Model name string with provider prefix (e.g., 'openai/gpt-5-nano') */ + fun modelName(modelName: String) = modelName(JsonField.of(modelName)) + + /** + * Sets [Builder.modelName] to an arbitrary JSON value. + * + * You should usually call [Builder.modelName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun modelName(modelName: JsonField) = apply { + this.modelName = modelName + } + + /** + * 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("vertex") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun provider(provider: JsonValue) = apply { this.provider = provider } + + /** Vertex provider-specific model configuration */ + fun providerOptions(providerOptions: ProviderOptions) = + providerOptions(JsonField.of(providerOptions)) + + /** + * Sets [Builder.providerOptions] to an arbitrary JSON value. + * + * You should usually call [Builder.providerOptions] with a well-typed + * [ProviderOptions] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun providerOptions(providerOptions: JsonField) = apply { + this.providerOptions = providerOptions + } + + /** API key for the model provider */ + fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + + /** + * Sets [Builder.apiKey] to an arbitrary JSON value. + * + * You should usually call [Builder.apiKey] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } + + /** Base URL for the model provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl } + + /** Custom headers sent with every request to the model provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun headers(headers: JsonField) = apply { this.headers = headers } + + 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 [VertexModelConfigObject]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .auth() + * .modelName() + * .providerOptions() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): VertexModelConfigObject = + VertexModelConfigObject( + checkRequired("auth", auth), + checkRequired("modelName", modelName), + provider, + checkRequired("providerOptions", providerOptions), + apiKey, + baseUrl, + headers, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): VertexModelConfigObject = apply { + if (validated) { + return@apply + } + + auth().validate() + modelName() + _provider().let { + if (it != JsonValue.from("vertex")) { + throw StagehandInvalidDataException( + "'provider' is invalid, received $it" + ) + } + } + providerOptions().validate() + apiKey() + baseUrl() + headers().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = + (auth.asKnown().getOrNull()?.validity() ?: 0) + + (if (modelName.asKnown().isPresent) 1 else 0) + + provider.let { if (it == JsonValue.from("vertex")) 1 else 0 } + + (providerOptions.asKnown().getOrNull()?.validity() ?: 0) + + (if (apiKey.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + /** Vertex provider authentication configuration */ + class Auth + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val credentials: JsonField, + private val type: JsonValue, + private val projectId: JsonField, + private val scopes: JsonField, + private val universeDomain: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("credentials") + @ExcludeMissing + credentials: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("projectId") + @ExcludeMissing + projectId: JsonField = JsonMissing.of(), + @JsonProperty("scopes") + @ExcludeMissing + scopes: JsonField = JsonMissing.of(), + @JsonProperty("universeDomain") + @ExcludeMissing + universeDomain: JsonField = JsonMissing.of(), + ) : this(credentials, type, projectId, scopes, universeDomain, mutableMapOf()) + + /** + * Google Cloud service account credentials + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with + * an unexpected value). + */ + fun credentials(): Credentials = credentials.getRequired("credentials") + + /** + * Use inline Google Cloud service account credentials for provider + * authentication + * + * Expected to always return the following: + * ```java + * JsonValue.from("googleServiceAccount") + * ``` + * + * 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 + + /** + * Google Cloud project ID used by google-auth-library + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun projectId(): Optional = projectId.getOptional("projectId") + + /** + * Google auth scopes for the desired API request + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun scopes(): Optional = scopes.getOptional("scopes") + + /** + * Google Cloud universe domain + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun universeDomain(): Optional = + universeDomain.getOptional("universeDomain") + + /** + * Returns the raw JSON value of [credentials]. + * + * Unlike [credentials], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("credentials") + @ExcludeMissing + fun _credentials(): JsonField = credentials + + /** + * Returns the raw JSON value of [projectId]. + * + * Unlike [projectId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("projectId") + @ExcludeMissing + fun _projectId(): JsonField = projectId + + /** + * Returns the raw JSON value of [scopes]. + * + * Unlike [scopes], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("scopes") + @ExcludeMissing + fun _scopes(): JsonField = scopes + + /** + * Returns the raw JSON value of [universeDomain]. + * + * Unlike [universeDomain], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("universeDomain") + @ExcludeMissing + fun _universeDomain(): JsonField = universeDomain + + @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 [Auth]. + * + * The following fields are required: + * ```java + * .credentials() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Auth]. */ + class Builder internal constructor() { + + private var credentials: JsonField? = null + private var type: JsonValue = JsonValue.from("googleServiceAccount") + private var projectId: JsonField = JsonMissing.of() + private var scopes: JsonField = JsonMissing.of() + private var universeDomain: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(auth: Auth) = apply { + credentials = auth.credentials + type = auth.type + projectId = auth.projectId + scopes = auth.scopes + universeDomain = auth.universeDomain + additionalProperties = auth.additionalProperties.toMutableMap() + } + + /** Google Cloud service account credentials */ + fun credentials(credentials: Credentials) = + credentials(JsonField.of(credentials)) + + /** + * Sets [Builder.credentials] to an arbitrary JSON value. + * + * You should usually call [Builder.credentials] with a well-typed + * [Credentials] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun credentials(credentials: JsonField) = apply { + this.credentials = credentials + } + + /** + * 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("googleServiceAccount") + * ``` + * + * This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** Google Cloud project ID used by google-auth-library */ + fun projectId(projectId: String) = projectId(JsonField.of(projectId)) + + /** + * Sets [Builder.projectId] to an arbitrary JSON value. + * + * You should usually call [Builder.projectId] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun projectId(projectId: JsonField) = apply { + this.projectId = projectId + } + + /** Google auth scopes for the desired API request */ + fun scopes(scopes: Scopes) = scopes(JsonField.of(scopes)) + + /** + * Sets [Builder.scopes] to an arbitrary JSON value. + * + * You should usually call [Builder.scopes] with a well-typed [Scopes] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun scopes(scopes: JsonField) = apply { this.scopes = scopes } + + /** Alias for calling [scopes] with `Scopes.ofString(string)`. */ + fun scopes(string: String) = scopes(Scopes.ofString(string)) + + /** Alias for calling [scopes] with `Scopes.ofStrings(strings)`. */ + fun scopesOfStrings(strings: List) = + scopes(Scopes.ofStrings(strings)) + + /** Google Cloud universe domain */ + fun universeDomain(universeDomain: String) = + universeDomain(JsonField.of(universeDomain)) + + /** + * Sets [Builder.universeDomain] to an arbitrary JSON value. + * + * You should usually call [Builder.universeDomain] with a well-typed + * [String] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun universeDomain(universeDomain: JsonField) = apply { + this.universeDomain = universeDomain + } + + 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 [Auth]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .credentials() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Auth = + Auth( + checkRequired("credentials", credentials), + type, + projectId, + scopes, + universeDomain, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Auth = apply { + if (validated) { + return@apply + } + + credentials().validate() + _type().let { + if (it != JsonValue.from("googleServiceAccount")) { + throw StagehandInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + projectId() + scopes().ifPresent { it.validate() } + universeDomain() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = + (credentials.asKnown().getOrNull()?.validity() ?: 0) + + type.let { + if (it == JsonValue.from("googleServiceAccount")) 1 else 0 + } + + (if (projectId.asKnown().isPresent) 1 else 0) + + (scopes.asKnown().getOrNull()?.validity() ?: 0) + + (if (universeDomain.asKnown().isPresent) 1 else 0) + + /** Google Cloud service account credentials */ + class Credentials + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val clientEmail: JsonField, + private val privateKey: JsonField, + private val authProviderX509CertUrl: JsonField, + private val authUri: JsonField, + private val clientId: JsonField, + private val clientX509CertUrl: JsonField, + private val privateKeyId: JsonField, + private val projectId: JsonField, + private val tokenUri: JsonField, + private val type: JsonField, + private val universeDomain: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("client_email") + @ExcludeMissing + clientEmail: JsonField = JsonMissing.of(), + @JsonProperty("private_key") + @ExcludeMissing + privateKey: JsonField = JsonMissing.of(), + @JsonProperty("auth_provider_x509_cert_url") + @ExcludeMissing + authProviderX509CertUrl: JsonField = JsonMissing.of(), + @JsonProperty("auth_uri") + @ExcludeMissing + authUri: JsonField = JsonMissing.of(), + @JsonProperty("client_id") + @ExcludeMissing + clientId: JsonField = JsonMissing.of(), + @JsonProperty("client_x509_cert_url") + @ExcludeMissing + clientX509CertUrl: JsonField = JsonMissing.of(), + @JsonProperty("private_key_id") + @ExcludeMissing + privateKeyId: JsonField = JsonMissing.of(), + @JsonProperty("project_id") + @ExcludeMissing + projectId: JsonField = JsonMissing.of(), + @JsonProperty("token_uri") + @ExcludeMissing + tokenUri: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonField = JsonMissing.of(), + @JsonProperty("universe_domain") + @ExcludeMissing + universeDomain: JsonField = JsonMissing.of(), + ) : this( + clientEmail, + privateKey, + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + mutableMapOf(), + ) + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun clientEmail(): String = clientEmail.getRequired("client_email") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun privateKey(): String = privateKey.getRequired("private_key") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun authProviderX509CertUrl(): Optional = + authProviderX509CertUrl.getOptional("auth_provider_x509_cert_url") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun authUri(): Optional = authUri.getOptional("auth_uri") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun clientId(): Optional = clientId.getOptional("client_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun clientX509CertUrl(): Optional = + clientX509CertUrl.getOptional("client_x509_cert_url") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun privateKeyId(): Optional = + privateKeyId.getOptional("private_key_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun projectId(): Optional = projectId.getOptional("project_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun tokenUri(): Optional = tokenUri.getOptional("token_uri") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun universeDomain(): Optional = + universeDomain.getOptional("universe_domain") + + /** + * Returns the raw JSON value of [clientEmail]. + * + * Unlike [clientEmail], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("client_email") + @ExcludeMissing + fun _clientEmail(): JsonField = clientEmail + + /** + * Returns the raw JSON value of [privateKey]. + * + * Unlike [privateKey], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("private_key") + @ExcludeMissing + fun _privateKey(): JsonField = privateKey + + /** + * Returns the raw JSON value of [authProviderX509CertUrl]. + * + * Unlike [authProviderX509CertUrl], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("auth_provider_x509_cert_url") + @ExcludeMissing + fun _authProviderX509CertUrl(): JsonField = authProviderX509CertUrl + + /** + * Returns the raw JSON value of [authUri]. + * + * Unlike [authUri], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("auth_uri") + @ExcludeMissing + fun _authUri(): JsonField = authUri + + /** + * Returns the raw JSON value of [clientId]. + * + * Unlike [clientId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("client_id") + @ExcludeMissing + fun _clientId(): JsonField = clientId + + /** + * Returns the raw JSON value of [clientX509CertUrl]. + * + * Unlike [clientX509CertUrl], this method doesn't throw if the JSON field + * has an unexpected type. + */ + @JsonProperty("client_x509_cert_url") + @ExcludeMissing + fun _clientX509CertUrl(): JsonField = clientX509CertUrl + + /** + * Returns the raw JSON value of [privateKeyId]. + * + * Unlike [privateKeyId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("private_key_id") + @ExcludeMissing + fun _privateKeyId(): JsonField = privateKeyId + + /** + * Returns the raw JSON value of [projectId]. + * + * Unlike [projectId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("project_id") + @ExcludeMissing + fun _projectId(): JsonField = projectId + + /** + * Returns the raw JSON value of [tokenUri]. + * + * Unlike [tokenUri], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("token_uri") + @ExcludeMissing + fun _tokenUri(): JsonField = tokenUri + + /** + * 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 [universeDomain]. + * + * Unlike [universeDomain], this method doesn't throw if the JSON field has + * an unexpected type. + */ + @JsonProperty("universe_domain") + @ExcludeMissing + fun _universeDomain(): JsonField = universeDomain + + @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 + * [Credentials]. + * + * The following fields are required: + * ```java + * .clientEmail() + * .privateKey() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Credentials]. */ + class Builder internal constructor() { + + private var clientEmail: JsonField? = null + private var privateKey: JsonField? = null + private var authProviderX509CertUrl: JsonField = + JsonMissing.of() + private var authUri: JsonField = JsonMissing.of() + private var clientId: JsonField = JsonMissing.of() + private var clientX509CertUrl: JsonField = JsonMissing.of() + private var privateKeyId: JsonField = JsonMissing.of() + private var projectId: JsonField = JsonMissing.of() + private var tokenUri: JsonField = JsonMissing.of() + private var type: JsonField = JsonMissing.of() + private var universeDomain: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(credentials: Credentials) = apply { + clientEmail = credentials.clientEmail + privateKey = credentials.privateKey + authProviderX509CertUrl = credentials.authProviderX509CertUrl + authUri = credentials.authUri + clientId = credentials.clientId + clientX509CertUrl = credentials.clientX509CertUrl + privateKeyId = credentials.privateKeyId + projectId = credentials.projectId + tokenUri = credentials.tokenUri + type = credentials.type + universeDomain = credentials.universeDomain + additionalProperties = + credentials.additionalProperties.toMutableMap() + } + + fun clientEmail(clientEmail: String) = + clientEmail(JsonField.of(clientEmail)) + + /** + * Sets [Builder.clientEmail] to an arbitrary JSON value. + * + * You should usually call [Builder.clientEmail] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun clientEmail(clientEmail: JsonField) = apply { + this.clientEmail = clientEmail + } + + fun privateKey(privateKey: String) = + privateKey(JsonField.of(privateKey)) + + /** + * Sets [Builder.privateKey] to an arbitrary JSON value. + * + * You should usually call [Builder.privateKey] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun privateKey(privateKey: JsonField) = apply { + this.privateKey = privateKey + } + + fun authProviderX509CertUrl(authProviderX509CertUrl: String) = + authProviderX509CertUrl(JsonField.of(authProviderX509CertUrl)) + + /** + * Sets [Builder.authProviderX509CertUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.authProviderX509CertUrl] with a + * well-typed [String] value instead. This method is primarily for + * setting the field to an undocumented or not yet supported value. + */ + fun authProviderX509CertUrl( + authProviderX509CertUrl: JsonField + ) = apply { this.authProviderX509CertUrl = authProviderX509CertUrl } + + fun authUri(authUri: String) = authUri(JsonField.of(authUri)) + + /** + * Sets [Builder.authUri] to an arbitrary JSON value. + * + * You should usually call [Builder.authUri] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun authUri(authUri: JsonField) = apply { + this.authUri = authUri + } + + fun clientId(clientId: String) = clientId(JsonField.of(clientId)) + + /** + * Sets [Builder.clientId] to an arbitrary JSON value. + * + * You should usually call [Builder.clientId] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun clientId(clientId: JsonField) = apply { + this.clientId = clientId + } + + fun clientX509CertUrl(clientX509CertUrl: String) = + clientX509CertUrl(JsonField.of(clientX509CertUrl)) + + /** + * Sets [Builder.clientX509CertUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.clientX509CertUrl] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun clientX509CertUrl(clientX509CertUrl: JsonField) = apply { + this.clientX509CertUrl = clientX509CertUrl + } + + fun privateKeyId(privateKeyId: String) = + privateKeyId(JsonField.of(privateKeyId)) + + /** + * Sets [Builder.privateKeyId] to an arbitrary JSON value. + * + * You should usually call [Builder.privateKeyId] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun privateKeyId(privateKeyId: JsonField) = apply { + this.privateKeyId = privateKeyId + } + + fun projectId(projectId: String) = projectId(JsonField.of(projectId)) + + /** + * Sets [Builder.projectId] to an arbitrary JSON value. + * + * You should usually call [Builder.projectId] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun projectId(projectId: JsonField) = apply { + this.projectId = projectId + } + + fun tokenUri(tokenUri: String) = tokenUri(JsonField.of(tokenUri)) + + /** + * Sets [Builder.tokenUri] to an arbitrary JSON value. + * + * You should usually call [Builder.tokenUri] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun tokenUri(tokenUri: JsonField) = apply { + this.tokenUri = tokenUri + } + + 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 universeDomain(universeDomain: String) = + universeDomain(JsonField.of(universeDomain)) + + /** + * Sets [Builder.universeDomain] to an arbitrary JSON value. + * + * You should usually call [Builder.universeDomain] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun universeDomain(universeDomain: JsonField) = apply { + this.universeDomain = universeDomain + } + + 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 [Credentials]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + * + * The following fields are required: + * ```java + * .clientEmail() + * .privateKey() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Credentials = + Credentials( + checkRequired("clientEmail", clientEmail), + checkRequired("privateKey", privateKey), + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Credentials = apply { + if (validated) { + return@apply + } + + clientEmail() + privateKey() + authProviderX509CertUrl() + authUri() + clientId() + clientX509CertUrl() + privateKeyId() + projectId() + tokenUri() + type().ifPresent { it.validate() } + universeDomain() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (clientEmail.asKnown().isPresent) 1 else 0) + + (if (privateKey.asKnown().isPresent) 1 else 0) + + (if (authProviderX509CertUrl.asKnown().isPresent) 1 else 0) + + (if (authUri.asKnown().isPresent) 1 else 0) + + (if (clientId.asKnown().isPresent) 1 else 0) + + (if (clientX509CertUrl.asKnown().isPresent) 1 else 0) + + (if (privateKeyId.asKnown().isPresent) 1 else 0) + + (if (projectId.asKnown().isPresent) 1 else 0) + + (if (tokenUri.asKnown().isPresent) 1 else 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + (if (universeDomain.asKnown().isPresent) 1 else 0) + + 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 SERVICE_ACCOUNT = of("service_account") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + SERVICE_ACCOUNT + } + + /** + * 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 { + SERVICE_ACCOUNT, + /** + * 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) { + SERVICE_ACCOUNT -> Value.SERVICE_ACCOUNT + 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 StagehandInvalidDataException if this class instance's value + * is a not a known member. + */ + fun known(): Known = + when (this) { + SERVICE_ACCOUNT -> Known.SERVICE_ACCOUNT + else -> + throw StagehandInvalidDataException("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 StagehandInvalidDataException if this class instance's value + * does not have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + StagehandInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API + * for existing fields. + * + * @throws StagehandInvalidDataException if any value type in this + * object doesn't match its expected type. + */ + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Type && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Credentials && + clientEmail == other.clientEmail && + privateKey == other.privateKey && + authProviderX509CertUrl == other.authProviderX509CertUrl && + authUri == other.authUri && + clientId == other.clientId && + clientX509CertUrl == other.clientX509CertUrl && + privateKeyId == other.privateKeyId && + projectId == other.projectId && + tokenUri == other.tokenUri && + type == other.type && + universeDomain == other.universeDomain && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + clientEmail, + privateKey, + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Credentials{clientEmail=$clientEmail, privateKey=$privateKey, authProviderX509CertUrl=$authProviderX509CertUrl, authUri=$authUri, clientId=$clientId, clientX509CertUrl=$clientX509CertUrl, privateKeyId=$privateKeyId, projectId=$projectId, tokenUri=$tokenUri, type=$type, universeDomain=$universeDomain, additionalProperties=$additionalProperties}" + } + + /** Google auth scopes for the desired API request */ + @JsonDeserialize(using = Scopes.Deserializer::class) + @JsonSerialize(using = Scopes.Serializer::class) + class Scopes + private constructor( + private val string: String? = null, + private val strings: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun strings(): Optional> = Optional.ofNullable(strings) + + fun isString(): Boolean = string != null + + fun isStrings(): Boolean = strings != null + + fun asString(): String = string.getOrThrow("string") + + fun asStrings(): List = strings.getOrThrow("strings") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the + * given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from + * the API, unless [visitor] overrides [Visitor.unknown]. To handle variants + * not known to this version of the SDK gracefully, consider overriding + * [Visitor.unknown]: + * ```java + * import com.browserbase.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = scopes.accept(new Scopes.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws StagehandInvalidDataException if [Visitor.unknown] is not + * overridden in [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + strings != null -> visitor.visitStrings(strings) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Scopes = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitStrings(strings: List) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 visitStrings(strings: List) = strings.size + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Scopes && + string == other.string && + strings == other.strings + } + + override fun hashCode(): Int = Objects.hash(string, strings) + + override fun toString(): String = + when { + string != null -> "Scopes{string=$string}" + strings != null -> "Scopes{strings=$strings}" + _json != null -> "Scopes{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Scopes") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Scopes(string = string) + + @JvmStatic + fun ofStrings(strings: List) = + Scopes(strings = strings.toImmutable()) + } + + /** + * An interface that defines how to map each variant of [Scopes] to a value + * of type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitStrings(strings: List): T + + /** + * Maps an unknown variant of [Scopes] to a value of type [T]. + * + * An instance of [Scopes] 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 StagehandInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw StagehandInvalidDataException("Unknown Scopes: $json") + } + } + + internal class Deserializer : BaseDeserializer(Scopes::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Scopes { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Scopes(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>()) + ?.let { Scopes(strings = 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 -> Scopes(_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(Scopes::class) { + + override fun serialize( + value: Scopes, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.strings != null -> generator.writeObject(value.strings) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Scopes") + } + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Auth && + credentials == other.credentials && + type == other.type && + projectId == other.projectId && + scopes == other.scopes && + universeDomain == other.universeDomain && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + credentials, + type, + projectId, + scopes, + universeDomain, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Auth{credentials=$credentials, type=$type, projectId=$projectId, scopes=$scopes, universeDomain=$universeDomain, additionalProperties=$additionalProperties}" + } + + /** Vertex provider-specific model configuration */ + class ProviderOptions + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val vertex: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("vertex") + @ExcludeMissing + vertex: JsonField = JsonMissing.of() + ) : this(vertex, mutableMapOf()) + + /** + * Vertex AI provider-specific settings + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with + * an unexpected value). + */ + fun vertex(): Vertex = vertex.getRequired("vertex") + + /** + * Returns the raw JSON value of [vertex]. + * + * Unlike [vertex], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("vertex") + @ExcludeMissing + fun _vertex(): JsonField = vertex + + @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 + * [ProviderOptions]. + * + * The following fields are required: + * ```java + * .vertex() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ProviderOptions]. */ + class Builder internal constructor() { + + private var vertex: JsonField? = null + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(providerOptions: ProviderOptions) = apply { + vertex = providerOptions.vertex + additionalProperties = + providerOptions.additionalProperties.toMutableMap() + } + + /** Vertex AI provider-specific settings */ + fun vertex(vertex: Vertex) = vertex(JsonField.of(vertex)) + + /** + * Sets [Builder.vertex] to an arbitrary JSON value. + * + * You should usually call [Builder.vertex] with a well-typed [Vertex] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun vertex(vertex: JsonField) = apply { this.vertex = vertex } + + 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 [ProviderOptions]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .vertex() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ProviderOptions = + ProviderOptions( + checkRequired("vertex", vertex), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): ProviderOptions = apply { + if (validated) { + return@apply + } + + vertex().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = (vertex.asKnown().getOrNull()?.validity() ?: 0) + + /** Vertex AI provider-specific settings */ + class Vertex + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val location: JsonField, + private val project: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("location") + @ExcludeMissing + location: JsonField = JsonMissing.of(), + @JsonProperty("project") + @ExcludeMissing + project: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + ) : this(location, project, baseUrl, headers, mutableMapOf()) + + /** + * Google Cloud location for Vertex AI models + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun location(): String = location.getRequired("location") + + /** + * Google Cloud project ID for Vertex AI models + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun project(): String = project.getRequired("project") + + /** + * Base URL for the Vertex AI provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the Vertex AI provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * Returns the raw JSON value of [location]. + * + * Unlike [location], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("location") + @ExcludeMissing + fun _location(): JsonField = location + + /** + * Returns the raw JSON value of [project]. + * + * Unlike [project], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("project") + @ExcludeMissing + fun _project(): JsonField = project + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("baseURL") + @ExcludeMissing + fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + @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 [Vertex]. + * + * The following fields are required: + * ```java + * .location() + * .project() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Vertex]. */ + class Builder internal constructor() { + + private var location: JsonField? = null + private var project: JsonField? = null + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(vertex: Vertex) = apply { + location = vertex.location + project = vertex.project + baseUrl = vertex.baseUrl + headers = vertex.headers + additionalProperties = vertex.additionalProperties.toMutableMap() + } + + /** Google Cloud location for Vertex AI models */ + fun location(location: String) = location(JsonField.of(location)) + + /** + * Sets [Builder.location] to an arbitrary JSON value. + * + * You should usually call [Builder.location] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun location(location: JsonField) = apply { + this.location = location + } + + /** Google Cloud project ID for Vertex AI models */ + fun project(project: String) = project(JsonField.of(project)) + + /** + * Sets [Builder.project] to an arbitrary JSON value. + * + * You should usually call [Builder.project] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun project(project: JsonField) = apply { + this.project = project + } + + /** Base URL for the Vertex AI provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { + this.baseUrl = baseUrl + } + + /** Custom headers sent with every request to the Vertex AI provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun headers(headers: JsonField) = apply { + this.headers = headers + } + + 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 [Vertex]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + * + * The following fields are required: + * ```java + * .location() + * .project() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Vertex = + Vertex( + checkRequired("location", location), + checkRequired("project", project), + baseUrl, + headers, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Vertex = apply { + if (validated) { + return@apply + } + + location() + project() + baseUrl() + headers().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (location.asKnown().isPresent) 1 else 0) + + (if (project.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + /** Custom headers sent with every request to the Vertex AI provider */ + class Headers + @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 + * [Headers]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = + headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API + * for existing fields. + * + * @throws StagehandInvalidDataException if any value type in this + * object doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Headers{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Vertex && + location == other.location && + project == other.project && + baseUrl == other.baseUrl && + headers == other.headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(location, project, baseUrl, headers, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Vertex{location=$location, project=$project, baseUrl=$baseUrl, headers=$headers, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ProviderOptions && + vertex == other.vertex && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(vertex, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ProviderOptions{vertex=$vertex, additionalProperties=$additionalProperties}" + } + + /** Custom headers sent with every request to the model provider */ + class Headers + @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 [Headers]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Headers{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is VertexModelConfigObject && + auth == other.auth && + modelName == other.modelName && + provider == other.provider && + providerOptions == other.providerOptions && + apiKey == other.apiKey && + baseUrl == other.baseUrl && + headers == other.headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + auth, + modelName, + provider, + providerOptions, + apiKey, + baseUrl, + headers, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "VertexModelConfigObject{auth=$auth, modelName=$modelName, provider=$provider, providerOptions=$providerOptions, apiKey=$apiKey, baseUrl=$baseUrl, headers=$headers, additionalProperties=$additionalProperties}" + } + + class GenericModelConfigObject + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val modelName: JsonField, + private val apiKey: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val provider: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("modelName") + @ExcludeMissing + modelName: JsonField = JsonMissing.of(), + @JsonProperty("apiKey") + @ExcludeMissing + apiKey: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + @JsonProperty("provider") + @ExcludeMissing + provider: JsonField = JsonMissing.of(), + ) : this(modelName, apiKey, baseUrl, headers, provider, mutableMapOf()) + + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun modelName(): String = modelName.getRequired("modelName") + + /** + * API key for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun apiKey(): Optional = apiKey.getOptional("apiKey") + + /** + * Base URL for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * AI provider for the model (or provide a baseURL endpoint instead) + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun provider(): Optional = provider.getOptional("provider") + + /** + * Returns the raw JSON value of [modelName]. + * + * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("modelName") + @ExcludeMissing + fun _modelName(): JsonField = modelName + + /** + * Returns the raw JSON value of [apiKey]. + * + * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + /** + * Returns the raw JSON value of [provider]. + * + * Unlike [provider], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("provider") + @ExcludeMissing + fun _provider(): JsonField = provider + + @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 + * [GenericModelConfigObject]. + * + * The following fields are required: + * ```java + * .modelName() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [GenericModelConfigObject]. */ + class Builder internal constructor() { + + private var modelName: JsonField? = null + private var apiKey: JsonField = JsonMissing.of() + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var provider: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(genericModelConfigObject: GenericModelConfigObject) = apply { + modelName = genericModelConfigObject.modelName + apiKey = genericModelConfigObject.apiKey + baseUrl = genericModelConfigObject.baseUrl + headers = genericModelConfigObject.headers + provider = genericModelConfigObject.provider + additionalProperties = + genericModelConfigObject.additionalProperties.toMutableMap() + } + + /** Model name string with provider prefix (e.g., 'openai/gpt-5-nano') */ + fun modelName(modelName: String) = modelName(JsonField.of(modelName)) + + /** + * Sets [Builder.modelName] to an arbitrary JSON value. + * + * You should usually call [Builder.modelName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun modelName(modelName: JsonField) = apply { + this.modelName = modelName + } + + /** API key for the model provider */ + fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + + /** + * Sets [Builder.apiKey] to an arbitrary JSON value. + * + * You should usually call [Builder.apiKey] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } + + /** Base URL for the model provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl } + + /** Custom headers sent with every request to the model provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun headers(headers: JsonField) = apply { this.headers = headers } + + /** AI provider for the model (or provide a baseURL endpoint instead) */ + fun provider(provider: Provider) = provider(JsonField.of(provider)) + + /** + * Sets [Builder.provider] to an arbitrary JSON value. + * + * You should usually call [Builder.provider] with a well-typed [Provider] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun provider(provider: JsonField) = apply { this.provider = provider } + + 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 [GenericModelConfigObject]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .modelName() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): GenericModelConfigObject = + GenericModelConfigObject( + checkRequired("modelName", modelName), + apiKey, + baseUrl, + headers, + provider, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): GenericModelConfigObject = apply { + if (validated) { + return@apply + } + + modelName() + apiKey() + baseUrl() + headers().ifPresent { it.validate() } + provider().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (modelName.asKnown().isPresent) 1 else 0) + + (if (apiKey.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + (provider.asKnown().getOrNull()?.validity() ?: 0) + + /** Custom headers sent with every request to the model provider */ + class Headers + @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 [Headers]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Headers{additionalProperties=$additionalProperties}" + } + + /** AI provider for the model (or provide a baseURL endpoint instead) */ + class Provider + @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 OPENAI = of("openai") + + @JvmField val ANTHROPIC = of("anthropic") + + @JvmField val GOOGLE = of("google") + + @JvmField val MICROSOFT = of("microsoft") + + @JvmField val BEDROCK = of("bedrock") + + @JvmStatic fun of(value: String) = Provider(JsonField.of(value)) + } + + /** An enum containing [Provider]'s known values. */ + enum class Known { + OPENAI, + ANTHROPIC, + GOOGLE, + MICROSOFT, + BEDROCK, + } + + /** + * An enum containing [Provider]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [Provider] 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 { + OPENAI, + ANTHROPIC, + GOOGLE, + MICROSOFT, + BEDROCK, + /** + * An enum member indicating that [Provider] 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) { + OPENAI -> Value.OPENAI + ANTHROPIC -> Value.ANTHROPIC + GOOGLE -> Value.GOOGLE + MICROSOFT -> Value.MICROSOFT + BEDROCK -> Value.BEDROCK + 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 StagehandInvalidDataException if this class instance's value is a not + * a known member. + */ + fun known(): Known = + when (this) { + OPENAI -> Known.OPENAI + ANTHROPIC -> Known.ANTHROPIC + GOOGLE -> Known.GOOGLE + MICROSOFT -> Known.MICROSOFT + BEDROCK -> Known.BEDROCK + else -> throw StagehandInvalidDataException("Unknown Provider: $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 StagehandInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + StagehandInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Provider = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Provider && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is GenericModelConfigObject && + modelName == other.modelName && + apiKey == other.apiKey && + baseUrl == other.baseUrl && + headers == other.headers && + provider == other.provider && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + modelName, + apiKey, + baseUrl, + headers, + provider, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "GenericModelConfigObject{modelName=$modelName, apiKey=$apiKey, baseUrl=$baseUrl, headers=$headers, provider=$provider, additionalProperties=$additionalProperties}" + } } override fun equals(other: Any?): Boolean { @@ -1119,19 +4387,27 @@ private constructor( return other is Options && ignoreSelectors == other.ignoreSelectors && model == other.model && + screenshot == other.screenshot && selector == other.selector && timeout == other.timeout && additionalProperties == other.additionalProperties } private val hashCode: Int by lazy { - Objects.hash(ignoreSelectors, model, selector, timeout, additionalProperties) + Objects.hash( + ignoreSelectors, + model, + screenshot, + selector, + timeout, + additionalProperties, + ) } override fun hashCode(): Int = hashCode override fun toString() = - "Options{ignoreSelectors=$ignoreSelectors, model=$model, selector=$selector, timeout=$timeout, additionalProperties=$additionalProperties}" + "Options{ignoreSelectors=$ignoreSelectors, model=$model, screenshot=$screenshot, selector=$selector, timeout=$timeout, additionalProperties=$additionalProperties}" } /** JSON Schema defining the structure of data to extract */ diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveParams.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveParams.kt index 8331e8b..87e37b2 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveParams.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveParams.kt @@ -11,6 +11,8 @@ import com.browserbase.api.core.JsonMissing import com.browserbase.api.core.JsonValue import com.browserbase.api.core.Params import com.browserbase.api.core.allMaxBy +import com.browserbase.api.core.checkKnown +import com.browserbase.api.core.checkRequired import com.browserbase.api.core.getOrThrow import com.browserbase.api.core.http.Headers import com.browserbase.api.core.http.QueryParams @@ -573,6 +575,7 @@ private constructor( class Options @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( + private val ignoreSelectors: JsonField>, private val model: JsonField, private val selector: JsonField, private val timeout: JsonField, @@ -582,6 +585,9 @@ private constructor( @JsonCreator private constructor( + @JsonProperty("ignoreSelectors") + @ExcludeMissing + ignoreSelectors: JsonField> = JsonMissing.of(), @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), @JsonProperty("selector") @ExcludeMissing @@ -590,7 +596,16 @@ private constructor( @JsonProperty("variables") @ExcludeMissing variables: JsonField = JsonMissing.of(), - ) : this(model, selector, timeout, variables, mutableMapOf()) + ) : this(ignoreSelectors, model, selector, timeout, variables, mutableMapOf()) + + /** + * Selectors for elements and subtrees that should be excluded from observation + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun ignoreSelectors(): Optional> = + ignoreSelectors.getOptional("ignoreSelectors") /** * Model configuration object or model name string (e.g., 'openai/gpt-5-nano') @@ -626,6 +641,16 @@ private constructor( */ fun variables(): Optional = variables.getOptional("variables") + /** + * Returns the raw JSON value of [ignoreSelectors]. + * + * Unlike [ignoreSelectors], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("ignoreSelectors") + @ExcludeMissing + fun _ignoreSelectors(): JsonField> = ignoreSelectors + /** * Returns the raw JSON value of [model]. * @@ -677,6 +702,7 @@ private constructor( /** A builder for [Options]. */ class Builder internal constructor() { + private var ignoreSelectors: JsonField>? = null private var model: JsonField = JsonMissing.of() private var selector: JsonField = JsonMissing.of() private var timeout: JsonField = JsonMissing.of() @@ -685,6 +711,7 @@ private constructor( @JvmSynthetic internal fun from(options: Options) = apply { + ignoreSelectors = options.ignoreSelectors.map { it.toMutableList() } model = options.model selector = options.selector timeout = options.timeout @@ -692,6 +719,33 @@ private constructor( additionalProperties = options.additionalProperties.toMutableMap() } + /** Selectors for elements and subtrees that should be excluded from observation */ + fun ignoreSelectors(ignoreSelectors: List) = + ignoreSelectors(JsonField.of(ignoreSelectors)) + + /** + * Sets [Builder.ignoreSelectors] to an arbitrary JSON value. + * + * You should usually call [Builder.ignoreSelectors] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun ignoreSelectors(ignoreSelectors: JsonField>) = apply { + this.ignoreSelectors = ignoreSelectors.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [ignoreSelectors]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addIgnoreSelector(ignoreSelector: String) = apply { + ignoreSelectors = + (ignoreSelectors ?: JsonField.of(mutableListOf())).also { + checkKnown("ignoreSelectors", it).add(ignoreSelector) + } + } + /** Model configuration object or model name string (e.g., 'openai/gpt-5-nano') */ fun model(model: Model) = model(JsonField.of(model)) @@ -704,8 +758,19 @@ private constructor( */ fun model(model: JsonField) = apply { this.model = model } - /** Alias for calling [model] with `Model.ofConfig(config)`. */ - fun model(config: ModelConfig) = model(Model.ofConfig(config)) + /** + * Alias for calling [model] with + * `Model.ofVertexModelConfigObject(vertexModelConfigObject)`. + */ + fun model(vertexModelConfigObject: Model.VertexModelConfigObject) = + model(Model.ofVertexModelConfigObject(vertexModelConfigObject)) + + /** + * Alias for calling [model] with + * `Model.ofGenericModelConfigObject(genericModelConfigObject)`. + */ + fun model(genericModelConfigObject: Model.GenericModelConfigObject) = + model(Model.ofGenericModelConfigObject(genericModelConfigObject)) /** Alias for calling [model] with `Model.ofString(string)`. */ fun model(string: String) = model(Model.ofString(string)) @@ -775,7 +840,14 @@ private constructor( * Further updates to this [Builder] will not mutate the returned instance. */ fun build(): Options = - Options(model, selector, timeout, variables, additionalProperties.toMutableMap()) + Options( + (ignoreSelectors ?: JsonMissing.of()).map { it.toImmutable() }, + model, + selector, + timeout, + variables, + additionalProperties.toMutableMap(), + ) } private var validated: Boolean = false @@ -794,6 +866,7 @@ private constructor( return@apply } + ignoreSelectors() model().ifPresent { it.validate() } selector() timeout() @@ -817,7 +890,8 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (model.asKnown().getOrNull()?.validity() ?: 0) + + (ignoreSelectors.asKnown().getOrNull()?.size ?: 0) + + (model.asKnown().getOrNull()?.validity() ?: 0) + (if (selector.asKnown().isPresent) 1 else 0) + (if (timeout.asKnown().isPresent) 1 else 0) + (variables.asKnown().getOrNull()?.validity() ?: 0) @@ -827,20 +901,31 @@ private constructor( @JsonSerialize(using = Model.Serializer::class) class Model private constructor( - private val config: ModelConfig? = null, + private val vertexModelConfigObject: VertexModelConfigObject? = null, + private val genericModelConfigObject: GenericModelConfigObject? = null, private val string: String? = null, private val _json: JsonValue? = null, ) { - fun config(): Optional = Optional.ofNullable(config) + fun vertexModelConfigObject(): Optional = + Optional.ofNullable(vertexModelConfigObject) + + fun genericModelConfigObject(): Optional = + Optional.ofNullable(genericModelConfigObject) fun string(): Optional = Optional.ofNullable(string) - fun isConfig(): Boolean = config != null + fun isVertexModelConfigObject(): Boolean = vertexModelConfigObject != null + + fun isGenericModelConfigObject(): Boolean = genericModelConfigObject != null fun isString(): Boolean = string != null - fun asConfig(): ModelConfig = config.getOrThrow("config") + fun asVertexModelConfigObject(): VertexModelConfigObject = + vertexModelConfigObject.getOrThrow("vertexModelConfigObject") + + fun asGenericModelConfigObject(): GenericModelConfigObject = + genericModelConfigObject.getOrThrow("genericModelConfigObject") fun asString(): String = string.getOrThrow("string") @@ -859,8 +944,8 @@ private constructor( * * Optional result = model.accept(new Model.Visitor>() { * @Override - * public Optional visitConfig(ModelConfig config) { - * return Optional.of(config.toString()); + * public Optional visitVertexModelConfigObject(VertexModelConfigObject vertexModelConfigObject) { + * return Optional.of(vertexModelConfigObject.toString()); * } * * // ... @@ -878,7 +963,10 @@ private constructor( */ fun accept(visitor: Visitor): T = when { - config != null -> visitor.visitConfig(config) + vertexModelConfigObject != null -> + visitor.visitVertexModelConfigObject(vertexModelConfigObject) + genericModelConfigObject != null -> + visitor.visitGenericModelConfigObject(genericModelConfigObject) string != null -> visitor.visitString(string) else -> visitor.unknown(_json) } @@ -902,8 +990,16 @@ private constructor( accept( object : Visitor { - override fun visitConfig(config: ModelConfig) { - config.validate() + override fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ) { + vertexModelConfigObject.validate() + } + + override fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ) { + genericModelConfigObject.validate() } override fun visitString(string: String) {} @@ -930,7 +1026,13 @@ private constructor( internal fun validity(): Int = accept( object : Visitor { - override fun visitConfig(config: ModelConfig) = config.validity() + override fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ) = vertexModelConfigObject.validity() + + override fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ) = genericModelConfigObject.validity() override fun visitString(string: String) = 1 @@ -943,14 +1045,21 @@ private constructor( return true } - return other is Model && config == other.config && string == other.string + return other is Model && + vertexModelConfigObject == other.vertexModelConfigObject && + genericModelConfigObject == other.genericModelConfigObject && + string == other.string } - override fun hashCode(): Int = Objects.hash(config, string) + override fun hashCode(): Int = + Objects.hash(vertexModelConfigObject, genericModelConfigObject, string) override fun toString(): String = when { - config != null -> "Model{config=$config}" + vertexModelConfigObject != null -> + "Model{vertexModelConfigObject=$vertexModelConfigObject}" + genericModelConfigObject != null -> + "Model{genericModelConfigObject=$genericModelConfigObject}" string != null -> "Model{string=$string}" _json != null -> "Model{_unknown=$_json}" else -> throw IllegalStateException("Invalid Model") @@ -958,7 +1067,13 @@ private constructor( companion object { - @JvmStatic fun ofConfig(config: ModelConfig) = Model(config = config) + @JvmStatic + fun ofVertexModelConfigObject(vertexModelConfigObject: VertexModelConfigObject) = + Model(vertexModelConfigObject = vertexModelConfigObject) + + @JvmStatic + fun ofGenericModelConfigObject(genericModelConfigObject: GenericModelConfigObject) = + Model(genericModelConfigObject = genericModelConfigObject) @JvmStatic fun ofString(string: String) = Model(string = string) } @@ -968,7 +1083,13 @@ private constructor( */ interface Visitor { - fun visitConfig(config: ModelConfig): T + fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ): T + + fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ): T fun visitString(string: String): T @@ -994,9 +1115,10 @@ private constructor( val bestMatches = sequenceOf( - tryDeserialize(node, jacksonTypeRef())?.let { - Model(config = it, _json = json) - }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { Model(vertexModelConfigObject = it, _json = json) }, + tryDeserialize(node, jacksonTypeRef()) + ?.let { Model(genericModelConfigObject = it, _json = json) }, tryDeserialize(node, jacksonTypeRef())?.let { Model(string = it, _json = json) }, @@ -1025,13 +1147,3179 @@ private constructor( provider: SerializerProvider, ) { when { - value.config != null -> generator.writeObject(value.config) + value.vertexModelConfigObject != null -> + generator.writeObject(value.vertexModelConfigObject) + value.genericModelConfigObject != null -> + generator.writeObject(value.genericModelConfigObject) value.string != null -> generator.writeObject(value.string) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid Model") } } } + + class VertexModelConfigObject + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val auth: JsonField, + private val modelName: JsonField, + private val provider: JsonValue, + private val providerOptions: JsonField, + private val apiKey: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("auth") @ExcludeMissing auth: JsonField = JsonMissing.of(), + @JsonProperty("modelName") + @ExcludeMissing + modelName: JsonField = JsonMissing.of(), + @JsonProperty("provider") + @ExcludeMissing + provider: JsonValue = JsonMissing.of(), + @JsonProperty("providerOptions") + @ExcludeMissing + providerOptions: JsonField = JsonMissing.of(), + @JsonProperty("apiKey") + @ExcludeMissing + apiKey: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + ) : this( + auth, + modelName, + provider, + providerOptions, + apiKey, + baseUrl, + headers, + mutableMapOf(), + ) + + /** + * Vertex provider authentication configuration + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun auth(): Auth = auth.getRequired("auth") + + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun modelName(): String = modelName.getRequired("modelName") + + /** + * Vertex AI model provider + * + * Expected to always return the following: + * ```java + * JsonValue.from("vertex") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonValue = provider + + /** + * Vertex provider-specific model configuration + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun providerOptions(): ProviderOptions = + providerOptions.getRequired("providerOptions") + + /** + * API key for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun apiKey(): Optional = apiKey.getOptional("apiKey") + + /** + * Base URL for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * Returns the raw JSON value of [auth]. + * + * Unlike [auth], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("auth") @ExcludeMissing fun _auth(): JsonField = auth + + /** + * Returns the raw JSON value of [modelName]. + * + * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("modelName") + @ExcludeMissing + fun _modelName(): JsonField = modelName + + /** + * Returns the raw JSON value of [providerOptions]. + * + * Unlike [providerOptions], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("providerOptions") + @ExcludeMissing + fun _providerOptions(): JsonField = providerOptions + + /** + * Returns the raw JSON value of [apiKey]. + * + * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + @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 + * [VertexModelConfigObject]. + * + * The following fields are required: + * ```java + * .auth() + * .modelName() + * .providerOptions() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [VertexModelConfigObject]. */ + class Builder internal constructor() { + + private var auth: JsonField? = null + private var modelName: JsonField? = null + private var provider: JsonValue = JsonValue.from("vertex") + private var providerOptions: JsonField? = null + private var apiKey: JsonField = JsonMissing.of() + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(vertexModelConfigObject: VertexModelConfigObject) = apply { + auth = vertexModelConfigObject.auth + modelName = vertexModelConfigObject.modelName + provider = vertexModelConfigObject.provider + providerOptions = vertexModelConfigObject.providerOptions + apiKey = vertexModelConfigObject.apiKey + baseUrl = vertexModelConfigObject.baseUrl + headers = vertexModelConfigObject.headers + additionalProperties = + vertexModelConfigObject.additionalProperties.toMutableMap() + } + + /** Vertex provider authentication configuration */ + fun auth(auth: Auth) = auth(JsonField.of(auth)) + + /** + * Sets [Builder.auth] to an arbitrary JSON value. + * + * You should usually call [Builder.auth] with a well-typed [Auth] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun auth(auth: JsonField) = apply { this.auth = auth } + + /** Model name string with provider prefix (e.g., 'openai/gpt-5-nano') */ + fun modelName(modelName: String) = modelName(JsonField.of(modelName)) + + /** + * Sets [Builder.modelName] to an arbitrary JSON value. + * + * You should usually call [Builder.modelName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun modelName(modelName: JsonField) = apply { + this.modelName = modelName + } + + /** + * 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("vertex") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun provider(provider: JsonValue) = apply { this.provider = provider } + + /** Vertex provider-specific model configuration */ + fun providerOptions(providerOptions: ProviderOptions) = + providerOptions(JsonField.of(providerOptions)) + + /** + * Sets [Builder.providerOptions] to an arbitrary JSON value. + * + * You should usually call [Builder.providerOptions] with a well-typed + * [ProviderOptions] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun providerOptions(providerOptions: JsonField) = apply { + this.providerOptions = providerOptions + } + + /** API key for the model provider */ + fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + + /** + * Sets [Builder.apiKey] to an arbitrary JSON value. + * + * You should usually call [Builder.apiKey] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } + + /** Base URL for the model provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl } + + /** Custom headers sent with every request to the model provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun headers(headers: JsonField) = apply { this.headers = headers } + + 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 [VertexModelConfigObject]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .auth() + * .modelName() + * .providerOptions() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): VertexModelConfigObject = + VertexModelConfigObject( + checkRequired("auth", auth), + checkRequired("modelName", modelName), + provider, + checkRequired("providerOptions", providerOptions), + apiKey, + baseUrl, + headers, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): VertexModelConfigObject = apply { + if (validated) { + return@apply + } + + auth().validate() + modelName() + _provider().let { + if (it != JsonValue.from("vertex")) { + throw StagehandInvalidDataException( + "'provider' is invalid, received $it" + ) + } + } + providerOptions().validate() + apiKey() + baseUrl() + headers().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = + (auth.asKnown().getOrNull()?.validity() ?: 0) + + (if (modelName.asKnown().isPresent) 1 else 0) + + provider.let { if (it == JsonValue.from("vertex")) 1 else 0 } + + (providerOptions.asKnown().getOrNull()?.validity() ?: 0) + + (if (apiKey.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + /** Vertex provider authentication configuration */ + class Auth + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val credentials: JsonField, + private val type: JsonValue, + private val projectId: JsonField, + private val scopes: JsonField, + private val universeDomain: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("credentials") + @ExcludeMissing + credentials: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("projectId") + @ExcludeMissing + projectId: JsonField = JsonMissing.of(), + @JsonProperty("scopes") + @ExcludeMissing + scopes: JsonField = JsonMissing.of(), + @JsonProperty("universeDomain") + @ExcludeMissing + universeDomain: JsonField = JsonMissing.of(), + ) : this(credentials, type, projectId, scopes, universeDomain, mutableMapOf()) + + /** + * Google Cloud service account credentials + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with + * an unexpected value). + */ + fun credentials(): Credentials = credentials.getRequired("credentials") + + /** + * Use inline Google Cloud service account credentials for provider + * authentication + * + * Expected to always return the following: + * ```java + * JsonValue.from("googleServiceAccount") + * ``` + * + * 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 + + /** + * Google Cloud project ID used by google-auth-library + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun projectId(): Optional = projectId.getOptional("projectId") + + /** + * Google auth scopes for the desired API request + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun scopes(): Optional = scopes.getOptional("scopes") + + /** + * Google Cloud universe domain + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun universeDomain(): Optional = + universeDomain.getOptional("universeDomain") + + /** + * Returns the raw JSON value of [credentials]. + * + * Unlike [credentials], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("credentials") + @ExcludeMissing + fun _credentials(): JsonField = credentials + + /** + * Returns the raw JSON value of [projectId]. + * + * Unlike [projectId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("projectId") + @ExcludeMissing + fun _projectId(): JsonField = projectId + + /** + * Returns the raw JSON value of [scopes]. + * + * Unlike [scopes], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("scopes") + @ExcludeMissing + fun _scopes(): JsonField = scopes + + /** + * Returns the raw JSON value of [universeDomain]. + * + * Unlike [universeDomain], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("universeDomain") + @ExcludeMissing + fun _universeDomain(): JsonField = universeDomain + + @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 [Auth]. + * + * The following fields are required: + * ```java + * .credentials() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Auth]. */ + class Builder internal constructor() { + + private var credentials: JsonField? = null + private var type: JsonValue = JsonValue.from("googleServiceAccount") + private var projectId: JsonField = JsonMissing.of() + private var scopes: JsonField = JsonMissing.of() + private var universeDomain: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(auth: Auth) = apply { + credentials = auth.credentials + type = auth.type + projectId = auth.projectId + scopes = auth.scopes + universeDomain = auth.universeDomain + additionalProperties = auth.additionalProperties.toMutableMap() + } + + /** Google Cloud service account credentials */ + fun credentials(credentials: Credentials) = + credentials(JsonField.of(credentials)) + + /** + * Sets [Builder.credentials] to an arbitrary JSON value. + * + * You should usually call [Builder.credentials] with a well-typed + * [Credentials] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun credentials(credentials: JsonField) = apply { + this.credentials = credentials + } + + /** + * 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("googleServiceAccount") + * ``` + * + * This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** Google Cloud project ID used by google-auth-library */ + fun projectId(projectId: String) = projectId(JsonField.of(projectId)) + + /** + * Sets [Builder.projectId] to an arbitrary JSON value. + * + * You should usually call [Builder.projectId] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun projectId(projectId: JsonField) = apply { + this.projectId = projectId + } + + /** Google auth scopes for the desired API request */ + fun scopes(scopes: Scopes) = scopes(JsonField.of(scopes)) + + /** + * Sets [Builder.scopes] to an arbitrary JSON value. + * + * You should usually call [Builder.scopes] with a well-typed [Scopes] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun scopes(scopes: JsonField) = apply { this.scopes = scopes } + + /** Alias for calling [scopes] with `Scopes.ofString(string)`. */ + fun scopes(string: String) = scopes(Scopes.ofString(string)) + + /** Alias for calling [scopes] with `Scopes.ofStrings(strings)`. */ + fun scopesOfStrings(strings: List) = + scopes(Scopes.ofStrings(strings)) + + /** Google Cloud universe domain */ + fun universeDomain(universeDomain: String) = + universeDomain(JsonField.of(universeDomain)) + + /** + * Sets [Builder.universeDomain] to an arbitrary JSON value. + * + * You should usually call [Builder.universeDomain] with a well-typed + * [String] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun universeDomain(universeDomain: JsonField) = apply { + this.universeDomain = universeDomain + } + + 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 [Auth]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .credentials() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Auth = + Auth( + checkRequired("credentials", credentials), + type, + projectId, + scopes, + universeDomain, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Auth = apply { + if (validated) { + return@apply + } + + credentials().validate() + _type().let { + if (it != JsonValue.from("googleServiceAccount")) { + throw StagehandInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + projectId() + scopes().ifPresent { it.validate() } + universeDomain() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = + (credentials.asKnown().getOrNull()?.validity() ?: 0) + + type.let { + if (it == JsonValue.from("googleServiceAccount")) 1 else 0 + } + + (if (projectId.asKnown().isPresent) 1 else 0) + + (scopes.asKnown().getOrNull()?.validity() ?: 0) + + (if (universeDomain.asKnown().isPresent) 1 else 0) + + /** Google Cloud service account credentials */ + class Credentials + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val clientEmail: JsonField, + private val privateKey: JsonField, + private val authProviderX509CertUrl: JsonField, + private val authUri: JsonField, + private val clientId: JsonField, + private val clientX509CertUrl: JsonField, + private val privateKeyId: JsonField, + private val projectId: JsonField, + private val tokenUri: JsonField, + private val type: JsonField, + private val universeDomain: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("client_email") + @ExcludeMissing + clientEmail: JsonField = JsonMissing.of(), + @JsonProperty("private_key") + @ExcludeMissing + privateKey: JsonField = JsonMissing.of(), + @JsonProperty("auth_provider_x509_cert_url") + @ExcludeMissing + authProviderX509CertUrl: JsonField = JsonMissing.of(), + @JsonProperty("auth_uri") + @ExcludeMissing + authUri: JsonField = JsonMissing.of(), + @JsonProperty("client_id") + @ExcludeMissing + clientId: JsonField = JsonMissing.of(), + @JsonProperty("client_x509_cert_url") + @ExcludeMissing + clientX509CertUrl: JsonField = JsonMissing.of(), + @JsonProperty("private_key_id") + @ExcludeMissing + privateKeyId: JsonField = JsonMissing.of(), + @JsonProperty("project_id") + @ExcludeMissing + projectId: JsonField = JsonMissing.of(), + @JsonProperty("token_uri") + @ExcludeMissing + tokenUri: JsonField = JsonMissing.of(), + @JsonProperty("type") + @ExcludeMissing + type: JsonField = JsonMissing.of(), + @JsonProperty("universe_domain") + @ExcludeMissing + universeDomain: JsonField = JsonMissing.of(), + ) : this( + clientEmail, + privateKey, + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + mutableMapOf(), + ) + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun clientEmail(): String = clientEmail.getRequired("client_email") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun privateKey(): String = privateKey.getRequired("private_key") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun authProviderX509CertUrl(): Optional = + authProviderX509CertUrl.getOptional("auth_provider_x509_cert_url") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun authUri(): Optional = authUri.getOptional("auth_uri") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun clientId(): Optional = clientId.getOptional("client_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun clientX509CertUrl(): Optional = + clientX509CertUrl.getOptional("client_x509_cert_url") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun privateKeyId(): Optional = + privateKeyId.getOptional("private_key_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun projectId(): Optional = projectId.getOptional("project_id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun tokenUri(): Optional = tokenUri.getOptional("token_uri") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun universeDomain(): Optional = + universeDomain.getOptional("universe_domain") + + /** + * Returns the raw JSON value of [clientEmail]. + * + * Unlike [clientEmail], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("client_email") + @ExcludeMissing + fun _clientEmail(): JsonField = clientEmail + + /** + * Returns the raw JSON value of [privateKey]. + * + * Unlike [privateKey], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("private_key") + @ExcludeMissing + fun _privateKey(): JsonField = privateKey + + /** + * Returns the raw JSON value of [authProviderX509CertUrl]. + * + * Unlike [authProviderX509CertUrl], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("auth_provider_x509_cert_url") + @ExcludeMissing + fun _authProviderX509CertUrl(): JsonField = authProviderX509CertUrl + + /** + * Returns the raw JSON value of [authUri]. + * + * Unlike [authUri], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("auth_uri") + @ExcludeMissing + fun _authUri(): JsonField = authUri + + /** + * Returns the raw JSON value of [clientId]. + * + * Unlike [clientId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("client_id") + @ExcludeMissing + fun _clientId(): JsonField = clientId + + /** + * Returns the raw JSON value of [clientX509CertUrl]. + * + * Unlike [clientX509CertUrl], this method doesn't throw if the JSON field + * has an unexpected type. + */ + @JsonProperty("client_x509_cert_url") + @ExcludeMissing + fun _clientX509CertUrl(): JsonField = clientX509CertUrl + + /** + * Returns the raw JSON value of [privateKeyId]. + * + * Unlike [privateKeyId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("private_key_id") + @ExcludeMissing + fun _privateKeyId(): JsonField = privateKeyId + + /** + * Returns the raw JSON value of [projectId]. + * + * Unlike [projectId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("project_id") + @ExcludeMissing + fun _projectId(): JsonField = projectId + + /** + * Returns the raw JSON value of [tokenUri]. + * + * Unlike [tokenUri], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("token_uri") + @ExcludeMissing + fun _tokenUri(): JsonField = tokenUri + + /** + * 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 [universeDomain]. + * + * Unlike [universeDomain], this method doesn't throw if the JSON field has + * an unexpected type. + */ + @JsonProperty("universe_domain") + @ExcludeMissing + fun _universeDomain(): JsonField = universeDomain + + @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 + * [Credentials]. + * + * The following fields are required: + * ```java + * .clientEmail() + * .privateKey() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Credentials]. */ + class Builder internal constructor() { + + private var clientEmail: JsonField? = null + private var privateKey: JsonField? = null + private var authProviderX509CertUrl: JsonField = + JsonMissing.of() + private var authUri: JsonField = JsonMissing.of() + private var clientId: JsonField = JsonMissing.of() + private var clientX509CertUrl: JsonField = JsonMissing.of() + private var privateKeyId: JsonField = JsonMissing.of() + private var projectId: JsonField = JsonMissing.of() + private var tokenUri: JsonField = JsonMissing.of() + private var type: JsonField = JsonMissing.of() + private var universeDomain: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(credentials: Credentials) = apply { + clientEmail = credentials.clientEmail + privateKey = credentials.privateKey + authProviderX509CertUrl = credentials.authProviderX509CertUrl + authUri = credentials.authUri + clientId = credentials.clientId + clientX509CertUrl = credentials.clientX509CertUrl + privateKeyId = credentials.privateKeyId + projectId = credentials.projectId + tokenUri = credentials.tokenUri + type = credentials.type + universeDomain = credentials.universeDomain + additionalProperties = + credentials.additionalProperties.toMutableMap() + } + + fun clientEmail(clientEmail: String) = + clientEmail(JsonField.of(clientEmail)) + + /** + * Sets [Builder.clientEmail] to an arbitrary JSON value. + * + * You should usually call [Builder.clientEmail] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun clientEmail(clientEmail: JsonField) = apply { + this.clientEmail = clientEmail + } + + fun privateKey(privateKey: String) = + privateKey(JsonField.of(privateKey)) + + /** + * Sets [Builder.privateKey] to an arbitrary JSON value. + * + * You should usually call [Builder.privateKey] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun privateKey(privateKey: JsonField) = apply { + this.privateKey = privateKey + } + + fun authProviderX509CertUrl(authProviderX509CertUrl: String) = + authProviderX509CertUrl(JsonField.of(authProviderX509CertUrl)) + + /** + * Sets [Builder.authProviderX509CertUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.authProviderX509CertUrl] with a + * well-typed [String] value instead. This method is primarily for + * setting the field to an undocumented or not yet supported value. + */ + fun authProviderX509CertUrl( + authProviderX509CertUrl: JsonField + ) = apply { this.authProviderX509CertUrl = authProviderX509CertUrl } + + fun authUri(authUri: String) = authUri(JsonField.of(authUri)) + + /** + * Sets [Builder.authUri] to an arbitrary JSON value. + * + * You should usually call [Builder.authUri] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun authUri(authUri: JsonField) = apply { + this.authUri = authUri + } + + fun clientId(clientId: String) = clientId(JsonField.of(clientId)) + + /** + * Sets [Builder.clientId] to an arbitrary JSON value. + * + * You should usually call [Builder.clientId] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun clientId(clientId: JsonField) = apply { + this.clientId = clientId + } + + fun clientX509CertUrl(clientX509CertUrl: String) = + clientX509CertUrl(JsonField.of(clientX509CertUrl)) + + /** + * Sets [Builder.clientX509CertUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.clientX509CertUrl] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun clientX509CertUrl(clientX509CertUrl: JsonField) = apply { + this.clientX509CertUrl = clientX509CertUrl + } + + fun privateKeyId(privateKeyId: String) = + privateKeyId(JsonField.of(privateKeyId)) + + /** + * Sets [Builder.privateKeyId] to an arbitrary JSON value. + * + * You should usually call [Builder.privateKeyId] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun privateKeyId(privateKeyId: JsonField) = apply { + this.privateKeyId = privateKeyId + } + + fun projectId(projectId: String) = projectId(JsonField.of(projectId)) + + /** + * Sets [Builder.projectId] to an arbitrary JSON value. + * + * You should usually call [Builder.projectId] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun projectId(projectId: JsonField) = apply { + this.projectId = projectId + } + + fun tokenUri(tokenUri: String) = tokenUri(JsonField.of(tokenUri)) + + /** + * Sets [Builder.tokenUri] to an arbitrary JSON value. + * + * You should usually call [Builder.tokenUri] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun tokenUri(tokenUri: JsonField) = apply { + this.tokenUri = tokenUri + } + + 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 universeDomain(universeDomain: String) = + universeDomain(JsonField.of(universeDomain)) + + /** + * Sets [Builder.universeDomain] to an arbitrary JSON value. + * + * You should usually call [Builder.universeDomain] with a well-typed + * [String] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun universeDomain(universeDomain: JsonField) = apply { + this.universeDomain = universeDomain + } + + 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 [Credentials]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + * + * The following fields are required: + * ```java + * .clientEmail() + * .privateKey() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Credentials = + Credentials( + checkRequired("clientEmail", clientEmail), + checkRequired("privateKey", privateKey), + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Credentials = apply { + if (validated) { + return@apply + } + + clientEmail() + privateKey() + authProviderX509CertUrl() + authUri() + clientId() + clientX509CertUrl() + privateKeyId() + projectId() + tokenUri() + type().ifPresent { it.validate() } + universeDomain() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (clientEmail.asKnown().isPresent) 1 else 0) + + (if (privateKey.asKnown().isPresent) 1 else 0) + + (if (authProviderX509CertUrl.asKnown().isPresent) 1 else 0) + + (if (authUri.asKnown().isPresent) 1 else 0) + + (if (clientId.asKnown().isPresent) 1 else 0) + + (if (clientX509CertUrl.asKnown().isPresent) 1 else 0) + + (if (privateKeyId.asKnown().isPresent) 1 else 0) + + (if (projectId.asKnown().isPresent) 1 else 0) + + (if (tokenUri.asKnown().isPresent) 1 else 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + (if (universeDomain.asKnown().isPresent) 1 else 0) + + 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 SERVICE_ACCOUNT = of("service_account") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + SERVICE_ACCOUNT + } + + /** + * 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 { + SERVICE_ACCOUNT, + /** + * 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) { + SERVICE_ACCOUNT -> Value.SERVICE_ACCOUNT + 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 StagehandInvalidDataException if this class instance's value + * is a not a known member. + */ + fun known(): Known = + when (this) { + SERVICE_ACCOUNT -> Known.SERVICE_ACCOUNT + else -> + throw StagehandInvalidDataException("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 StagehandInvalidDataException if this class instance's value + * does not have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + StagehandInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API + * for existing fields. + * + * @throws StagehandInvalidDataException if any value type in this + * object doesn't match its expected type. + */ + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Type && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Credentials && + clientEmail == other.clientEmail && + privateKey == other.privateKey && + authProviderX509CertUrl == other.authProviderX509CertUrl && + authUri == other.authUri && + clientId == other.clientId && + clientX509CertUrl == other.clientX509CertUrl && + privateKeyId == other.privateKeyId && + projectId == other.projectId && + tokenUri == other.tokenUri && + type == other.type && + universeDomain == other.universeDomain && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + clientEmail, + privateKey, + authProviderX509CertUrl, + authUri, + clientId, + clientX509CertUrl, + privateKeyId, + projectId, + tokenUri, + type, + universeDomain, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Credentials{clientEmail=$clientEmail, privateKey=$privateKey, authProviderX509CertUrl=$authProviderX509CertUrl, authUri=$authUri, clientId=$clientId, clientX509CertUrl=$clientX509CertUrl, privateKeyId=$privateKeyId, projectId=$projectId, tokenUri=$tokenUri, type=$type, universeDomain=$universeDomain, additionalProperties=$additionalProperties}" + } + + /** Google auth scopes for the desired API request */ + @JsonDeserialize(using = Scopes.Deserializer::class) + @JsonSerialize(using = Scopes.Serializer::class) + class Scopes + private constructor( + private val string: String? = null, + private val strings: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun strings(): Optional> = Optional.ofNullable(strings) + + fun isString(): Boolean = string != null + + fun isStrings(): Boolean = strings != null + + fun asString(): String = string.getOrThrow("string") + + fun asStrings(): List = strings.getOrThrow("strings") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the + * given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from + * the API, unless [visitor] overrides [Visitor.unknown]. To handle variants + * not known to this version of the SDK gracefully, consider overriding + * [Visitor.unknown]: + * ```java + * import com.browserbase.api.core.JsonValue; + * import java.util.Optional; + * + * Optional result = scopes.accept(new Scopes.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws StagehandInvalidDataException if [Visitor.unknown] is not + * overridden in [visitor] and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + strings != null -> visitor.visitStrings(strings) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Scopes = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitStrings(strings: List) {} + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 visitStrings(strings: List) = strings.size + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Scopes && + string == other.string && + strings == other.strings + } + + override fun hashCode(): Int = Objects.hash(string, strings) + + override fun toString(): String = + when { + string != null -> "Scopes{string=$string}" + strings != null -> "Scopes{strings=$strings}" + _json != null -> "Scopes{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Scopes") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Scopes(string = string) + + @JvmStatic + fun ofStrings(strings: List) = + Scopes(strings = strings.toImmutable()) + } + + /** + * An interface that defines how to map each variant of [Scopes] to a value + * of type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitStrings(strings: List): T + + /** + * Maps an unknown variant of [Scopes] to a value of type [T]. + * + * An instance of [Scopes] 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 StagehandInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw StagehandInvalidDataException("Unknown Scopes: $json") + } + } + + internal class Deserializer : BaseDeserializer(Scopes::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Scopes { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Scopes(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>()) + ?.let { Scopes(strings = 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 -> Scopes(_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(Scopes::class) { + + override fun serialize( + value: Scopes, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.strings != null -> generator.writeObject(value.strings) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Scopes") + } + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Auth && + credentials == other.credentials && + type == other.type && + projectId == other.projectId && + scopes == other.scopes && + universeDomain == other.universeDomain && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + credentials, + type, + projectId, + scopes, + universeDomain, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Auth{credentials=$credentials, type=$type, projectId=$projectId, scopes=$scopes, universeDomain=$universeDomain, additionalProperties=$additionalProperties}" + } + + /** Vertex provider-specific model configuration */ + class ProviderOptions + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val vertex: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("vertex") + @ExcludeMissing + vertex: JsonField = JsonMissing.of() + ) : this(vertex, mutableMapOf()) + + /** + * Vertex AI provider-specific settings + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded with + * an unexpected value). + */ + fun vertex(): Vertex = vertex.getRequired("vertex") + + /** + * Returns the raw JSON value of [vertex]. + * + * Unlike [vertex], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("vertex") + @ExcludeMissing + fun _vertex(): JsonField = vertex + + @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 + * [ProviderOptions]. + * + * The following fields are required: + * ```java + * .vertex() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ProviderOptions]. */ + class Builder internal constructor() { + + private var vertex: JsonField? = null + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(providerOptions: ProviderOptions) = apply { + vertex = providerOptions.vertex + additionalProperties = + providerOptions.additionalProperties.toMutableMap() + } + + /** Vertex AI provider-specific settings */ + fun vertex(vertex: Vertex) = vertex(JsonField.of(vertex)) + + /** + * Sets [Builder.vertex] to an arbitrary JSON value. + * + * You should usually call [Builder.vertex] with a well-typed [Vertex] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun vertex(vertex: JsonField) = apply { this.vertex = vertex } + + 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 [ProviderOptions]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .vertex() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ProviderOptions = + ProviderOptions( + checkRequired("vertex", vertex), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): ProviderOptions = apply { + if (validated) { + return@apply + } + + vertex().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 = (vertex.asKnown().getOrNull()?.validity() ?: 0) + + /** Vertex AI provider-specific settings */ + class Vertex + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val location: JsonField, + private val project: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("location") + @ExcludeMissing + location: JsonField = JsonMissing.of(), + @JsonProperty("project") + @ExcludeMissing + project: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + ) : this(location, project, baseUrl, headers, mutableMapOf()) + + /** + * Google Cloud location for Vertex AI models + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun location(): String = location.getRequired("location") + + /** + * Google Cloud project ID for Vertex AI models + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type or is unexpectedly missing or null (e.g. if the server responded + * with an unexpected value). + */ + fun project(): String = project.getRequired("project") + + /** + * Base URL for the Vertex AI provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the Vertex AI provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * Returns the raw JSON value of [location]. + * + * Unlike [location], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("location") + @ExcludeMissing + fun _location(): JsonField = location + + /** + * Returns the raw JSON value of [project]. + * + * Unlike [project], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("project") + @ExcludeMissing + fun _project(): JsonField = project + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("baseURL") + @ExcludeMissing + fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + @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 [Vertex]. + * + * The following fields are required: + * ```java + * .location() + * .project() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Vertex]. */ + class Builder internal constructor() { + + private var location: JsonField? = null + private var project: JsonField? = null + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(vertex: Vertex) = apply { + location = vertex.location + project = vertex.project + baseUrl = vertex.baseUrl + headers = vertex.headers + additionalProperties = vertex.additionalProperties.toMutableMap() + } + + /** Google Cloud location for Vertex AI models */ + fun location(location: String) = location(JsonField.of(location)) + + /** + * Sets [Builder.location] to an arbitrary JSON value. + * + * You should usually call [Builder.location] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun location(location: JsonField) = apply { + this.location = location + } + + /** Google Cloud project ID for Vertex AI models */ + fun project(project: String) = project(JsonField.of(project)) + + /** + * Sets [Builder.project] to an arbitrary JSON value. + * + * You should usually call [Builder.project] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun project(project: JsonField) = apply { + this.project = project + } + + /** Base URL for the Vertex AI provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { + this.baseUrl = baseUrl + } + + /** Custom headers sent with every request to the Vertex AI provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun headers(headers: JsonField) = apply { + this.headers = headers + } + + 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 [Vertex]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + * + * The following fields are required: + * ```java + * .location() + * .project() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Vertex = + Vertex( + checkRequired("location", location), + checkRequired("project", project), + baseUrl, + headers, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Vertex = apply { + if (validated) { + return@apply + } + + location() + project() + baseUrl() + headers().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (location.asKnown().isPresent) 1 else 0) + + (if (project.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + /** Custom headers sent with every request to the Vertex AI provider */ + class Headers + @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 + * [Headers]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = + headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their + * expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API + * for existing fields. + * + * @throws StagehandInvalidDataException if any value type in this + * object doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Headers{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Vertex && + location == other.location && + project == other.project && + baseUrl == other.baseUrl && + headers == other.headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(location, project, baseUrl, headers, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Vertex{location=$location, project=$project, baseUrl=$baseUrl, headers=$headers, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ProviderOptions && + vertex == other.vertex && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(vertex, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ProviderOptions{vertex=$vertex, additionalProperties=$additionalProperties}" + } + + /** Custom headers sent with every request to the model provider */ + class Headers + @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 [Headers]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Headers{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is VertexModelConfigObject && + auth == other.auth && + modelName == other.modelName && + provider == other.provider && + providerOptions == other.providerOptions && + apiKey == other.apiKey && + baseUrl == other.baseUrl && + headers == other.headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + auth, + modelName, + provider, + providerOptions, + apiKey, + baseUrl, + headers, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "VertexModelConfigObject{auth=$auth, modelName=$modelName, provider=$provider, providerOptions=$providerOptions, apiKey=$apiKey, baseUrl=$baseUrl, headers=$headers, additionalProperties=$additionalProperties}" + } + + class GenericModelConfigObject + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val modelName: JsonField, + private val apiKey: JsonField, + private val baseUrl: JsonField, + private val headers: JsonField, + private val provider: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("modelName") + @ExcludeMissing + modelName: JsonField = JsonMissing.of(), + @JsonProperty("apiKey") + @ExcludeMissing + apiKey: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") + @ExcludeMissing + baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("headers") + @ExcludeMissing + headers: JsonField = JsonMissing.of(), + @JsonProperty("provider") + @ExcludeMissing + provider: JsonField = JsonMissing.of(), + ) : this(modelName, apiKey, baseUrl, headers, provider, mutableMapOf()) + + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun modelName(): String = modelName.getRequired("modelName") + + /** + * API key for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun apiKey(): Optional = apiKey.getOptional("apiKey") + + /** + * Base URL for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Custom headers sent with every request to the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * AI provider for the model (or provide a baseURL endpoint instead) + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun provider(): Optional = provider.getOptional("provider") + + /** + * Returns the raw JSON value of [modelName]. + * + * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("modelName") + @ExcludeMissing + fun _modelName(): JsonField = modelName + + /** + * Returns the raw JSON value of [apiKey]. + * + * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey + + /** + * Returns the raw JSON value of [baseUrl]. + * + * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("headers") + @ExcludeMissing + fun _headers(): JsonField = headers + + /** + * Returns the raw JSON value of [provider]. + * + * Unlike [provider], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("provider") + @ExcludeMissing + fun _provider(): JsonField = provider + + @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 + * [GenericModelConfigObject]. + * + * The following fields are required: + * ```java + * .modelName() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [GenericModelConfigObject]. */ + class Builder internal constructor() { + + private var modelName: JsonField? = null + private var apiKey: JsonField = JsonMissing.of() + private var baseUrl: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var provider: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(genericModelConfigObject: GenericModelConfigObject) = apply { + modelName = genericModelConfigObject.modelName + apiKey = genericModelConfigObject.apiKey + baseUrl = genericModelConfigObject.baseUrl + headers = genericModelConfigObject.headers + provider = genericModelConfigObject.provider + additionalProperties = + genericModelConfigObject.additionalProperties.toMutableMap() + } + + /** Model name string with provider prefix (e.g., 'openai/gpt-5-nano') */ + fun modelName(modelName: String) = modelName(JsonField.of(modelName)) + + /** + * Sets [Builder.modelName] to an arbitrary JSON value. + * + * You should usually call [Builder.modelName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun modelName(modelName: JsonField) = apply { + this.modelName = modelName + } + + /** API key for the model provider */ + fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + + /** + * Sets [Builder.apiKey] to an arbitrary JSON value. + * + * You should usually call [Builder.apiKey] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } + + /** Base URL for the model provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + + /** + * Sets [Builder.baseUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.baseUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl } + + /** Custom headers sent with every request to the model provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun headers(headers: JsonField) = apply { this.headers = headers } + + /** AI provider for the model (or provide a baseURL endpoint instead) */ + fun provider(provider: Provider) = provider(JsonField.of(provider)) + + /** + * Sets [Builder.provider] to an arbitrary JSON value. + * + * You should usually call [Builder.provider] with a well-typed [Provider] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun provider(provider: JsonField) = apply { this.provider = provider } + + 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 [GenericModelConfigObject]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .modelName() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): GenericModelConfigObject = + GenericModelConfigObject( + checkRequired("modelName", modelName), + apiKey, + baseUrl, + headers, + provider, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws StagehandInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): GenericModelConfigObject = apply { + if (validated) { + return@apply + } + + modelName() + apiKey() + baseUrl() + headers().ifPresent { it.validate() } + provider().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 (modelName.asKnown().isPresent) 1 else 0) + + (if (apiKey.asKnown().isPresent) 1 else 0) + + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + (provider.asKnown().getOrNull()?.validity() ?: 0) + + /** Custom headers sent with every request to the model provider */ + class Headers + @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 [Headers]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Headers && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Headers{additionalProperties=$additionalProperties}" + } + + /** AI provider for the model (or provide a baseURL endpoint instead) */ + class Provider + @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 OPENAI = of("openai") + + @JvmField val ANTHROPIC = of("anthropic") + + @JvmField val GOOGLE = of("google") + + @JvmField val MICROSOFT = of("microsoft") + + @JvmField val BEDROCK = of("bedrock") + + @JvmStatic fun of(value: String) = Provider(JsonField.of(value)) + } + + /** An enum containing [Provider]'s known values. */ + enum class Known { + OPENAI, + ANTHROPIC, + GOOGLE, + MICROSOFT, + BEDROCK, + } + + /** + * An enum containing [Provider]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [Provider] 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 { + OPENAI, + ANTHROPIC, + GOOGLE, + MICROSOFT, + BEDROCK, + /** + * An enum member indicating that [Provider] 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) { + OPENAI -> Value.OPENAI + ANTHROPIC -> Value.ANTHROPIC + GOOGLE -> Value.GOOGLE + MICROSOFT -> Value.MICROSOFT + BEDROCK -> Value.BEDROCK + 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 StagehandInvalidDataException if this class instance's value is a not + * a known member. + */ + fun known(): Known = + when (this) { + OPENAI -> Known.OPENAI + ANTHROPIC -> Known.ANTHROPIC + GOOGLE -> Known.GOOGLE + MICROSOFT -> Known.MICROSOFT + BEDROCK -> Known.BEDROCK + else -> throw StagehandInvalidDataException("Unknown Provider: $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 StagehandInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + StagehandInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws StagehandInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Provider = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + 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 other is Provider && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is GenericModelConfigObject && + modelName == other.modelName && + apiKey == other.apiKey && + baseUrl == other.baseUrl && + headers == other.headers && + provider == other.provider && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + modelName, + apiKey, + baseUrl, + headers, + provider, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "GenericModelConfigObject{modelName=$modelName, apiKey=$apiKey, baseUrl=$baseUrl, headers=$headers, provider=$provider, additionalProperties=$additionalProperties}" + } } /** @@ -1157,6 +4445,7 @@ private constructor( } return other is Options && + ignoreSelectors == other.ignoreSelectors && model == other.model && selector == other.selector && timeout == other.timeout && @@ -1165,13 +4454,13 @@ private constructor( } private val hashCode: Int by lazy { - Objects.hash(model, selector, timeout, variables, additionalProperties) + Objects.hash(ignoreSelectors, model, selector, timeout, variables, additionalProperties) } override fun hashCode(): Int = hashCode override fun toString() = - "Options{model=$model, selector=$selector, timeout=$timeout, variables=$variables, additionalProperties=$additionalProperties}" + "Options{ignoreSelectors=$ignoreSelectors, model=$model, selector=$selector, timeout=$timeout, variables=$variables, additionalProperties=$additionalProperties}" } /** Whether to stream the response via SSE */ diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/core/http/LoggingHttpClientTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/core/http/LoggingHttpClientTest.kt new file mode 100644 index 0000000..a35c99d --- /dev/null +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/core/http/LoggingHttpClientTest.kt @@ -0,0 +1,1009 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.browserbase.api.core.http + +import com.browserbase.api.core.LogLevel +import com.browserbase.api.core.RequestOptions +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.io.PrintStream +import java.nio.charset.StandardCharsets +import java.time.Clock +import java.time.Instant +import java.time.ZoneOffset +import java.util.concurrent.CompletableFuture +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.parallel.ResourceLock +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource + +@ResourceLock("stderr") +internal class LoggingHttpClientTest { + + private lateinit var originalErr: PrintStream + private lateinit var errContent: ByteArrayOutputStream + + @BeforeEach + fun beforeEach() { + originalErr = System.err + errContent = ByteArrayOutputStream() + System.setErr(PrintStream(errContent)) + } + + @AfterEach + fun afterEach() { + System.setErr(originalErr) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun offLevel_noOutput(async: Boolean) { + val client = loggingClient(fakeHttpClient(), LogLevel.OFF) + + val response = client.execute(simpleGetRequest(), async).apply { body().readBytes() } + + assertThat(response.statusCode()).isEqualTo(200) + assertThat(stderrOutput()).isEmpty() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun infoLevel_logsGetRequest(async: Boolean) { + val client = loggingClient(fakeHttpClient(), LogLevel.INFO) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- 200 (0s) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun infoLevel_logsPostRequestWithBodySize(async: Boolean) { + val client = loggingClient(fakeHttpClient(), LogLevel.INFO) + + client.execute(postRequestWithBody("""{"key":"value"}"""), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> POST https://api.example.com/v1/resources (15-byte body) + |<-- 200 (0s) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun infoLevel_logsRequestWithUnknownLengthBody(async: Boolean) { + val client = loggingClient(fakeHttpClient(), LogLevel.INFO) + + client + .execute(postRequestWithBody("""{"key":"value"}""", contentLength = -1L), async) + .body() + .readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> POST https://api.example.com/v1/resources (unknown-length body) + |<-- 200 (0s) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun infoLevel_logsResponseStatusAndDuration(async: Boolean) { + val clock = + clockFrom( + Instant.parse("1998-04-21T00:00:00Z"), + Instant.parse("1998-04-21T00:00:01.234Z"), + ) + val client = loggingClient(fakeHttpClient(statusCode = 201), LogLevel.INFO, clock) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- 201 (1s 234ms) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun infoLevel_logsResponseContentLength(async: Boolean) { + val headers = + Headers.builder().put("Content-Length", "42").put("Content-Type", "text/plain").build() + val client = loggingClient(fakeHttpClient(responseHeaders = headers), LogLevel.INFO) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- 200 (0s, 42-byte body) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun infoLevel_doesNotLogHeaders(async: Boolean) { + val headers = Headers.builder().put("X-Custom", "visible").build() + val client = loggingClient(fakeHttpClient(responseHeaders = headers), LogLevel.INFO) + + client + .execute( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("v1") + .putHeader("X-Request-Custom", "req-value") + .build(), + async, + ) + .body() + .readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1 + |<-- 200 (0s) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsGetWithEndMarker(async: Boolean) { + val client = loggingClient(fakeHttpClient(), LogLevel.DEBUG) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |<-- END HTTP (0-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsRequestAndResponseHeaders(async: Boolean) { + val responseHeaders = + Headers.builder() + .put("X-Response-Id", "abc-123") + .put("Content-Type", "text/plain") + .build() + val client = + loggingClient(fakeHttpClient(responseHeaders = responseHeaders), LogLevel.DEBUG) + + client + .execute( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("test") + .putHeader("X-Custom", "my-value") + .build(), + async, + ) + .body() + .readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/test + |X-Custom: my-value + |--> END GET + | + |<-- 200 (0s) + |Content-Type: text/plain + |X-Response-Id: abc-123 + |<-- END HTTP (0-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_redactsSensitiveHeaders(async: Boolean) { + val client = + loggingClient( + fakeHttpClient(), + LogLevel.DEBUG, + redactedHeaders = setOf("Authorization", "X-Secret"), + ) + + client + .execute( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("test") + .putHeader("Authorization", "Bearer token-123") + .putHeader("X-Secret", "secret-value") + .putHeader("X-Public", "public-value") + .build(), + async, + ) + .body() + .readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/test + |Authorization: ██ + |X-Public: public-value + |X-Secret: ██ + |--> END GET + | + |<-- 200 (0s) + |<-- END HTTP (0-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_redactsHeadersCaseInsensitively(async: Boolean) { + val client = + loggingClient( + fakeHttpClient(), + LogLevel.DEBUG, + redactedHeaders = setOf("Authorization"), + ) + + client + .execute( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("test") + .putHeader("authorization", "Bearer secret") + .build(), + async, + ) + .body() + .readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/test + |authorization: ██ + |--> END GET + | + |<-- 200 (0s) + |<-- END HTTP (0-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsRequestBody(async: Boolean) { + val client = loggingClient(fakeHttpClient(), LogLevel.DEBUG) + val body = """{"name":"test","value":42}""" + + client.execute(postRequestWithBody(body), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> POST https://api.example.com/v1/resources (26-byte body) + | + |{"name":"test","value":42} + |--> END POST (26-byte body) + | + |<-- 200 (0s) + |<-- END HTTP (0-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsResponseBody(async: Boolean) { + val responseBody = """{"id":1,"status":"ok"}""" + val headers = Headers.builder().put("Content-Type", "application/json").build() + val client = + loggingClient( + fakeHttpClient( + responseHeaders = headers, + responseBody = responseBody.toByteArray(StandardCharsets.UTF_8), + ), + LogLevel.DEBUG, + ) + + val response = client.execute(simpleGetRequest(), async) + val body = response.body().readBytes().toString(StandardCharsets.UTF_8) + + assertThat(body).isEqualTo(responseBody) + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |Content-Type: application/json + | + |{"id":1,"status":"ok"} + |<-- END HTTP (22-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsBinaryResponseBodyAsOmitted(async: Boolean) { + val binaryBody = ByteArray(256) { it.toByte() } + val client = loggingClient(fakeHttpClient(responseBody = binaryBody), LogLevel.DEBUG) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + | + |(binary body omitted) + |<-- END HTTP (256-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsMultilineResponseBody(async: Boolean) { + val multilineBody = "line1\nline2\nline3" + val headers = Headers.builder().put("Content-Type", "text/plain; charset=utf-8").build() + val client = + loggingClient( + fakeHttpClient( + responseHeaders = headers, + responseBody = multilineBody.toByteArray(StandardCharsets.UTF_8), + ), + LogLevel.DEBUG, + ) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |Content-Type: text/plain; charset=utf-8 + | + |line1 + |line2 + |line3 + |<-- END HTTP (17-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsResponseBodyWithExplicitCharset(async: Boolean) { + val responseBody = "héllo wörld" + val headers = Headers.builder().put("Content-Type", "text/plain; charset=utf-8").build() + val client = + loggingClient( + fakeHttpClient( + responseHeaders = headers, + responseBody = responseBody.toByteArray(StandardCharsets.UTF_8), + ), + LogLevel.DEBUG, + ) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |Content-Type: text/plain; charset=utf-8 + | + |héllo wörld + |<-- END HTTP (13-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsResponseBodyWithNoContentType(async: Boolean) { + val responseBody = "plain text body" + val client = + loggingClient( + fakeHttpClient(responseBody = responseBody.toByteArray(StandardCharsets.UTF_8)), + LogLevel.DEBUG, + ) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + | + |plain text body + |<-- END HTTP (15-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsEmptyResponseBody(async: Boolean) { + val client = loggingClient(fakeHttpClient(), LogLevel.DEBUG) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |<-- END HTTP (0-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsEndHttpMarkerOnEarlyClose(async: Boolean) { + val responseBody = """{"id":1,"status":"ok"}""" + val headers = Headers.builder().put("Content-Type", "application/json").build() + val client = + loggingClient( + fakeHttpClient( + responseHeaders = headers, + responseBody = responseBody.toByteArray(StandardCharsets.UTF_8), + ), + LogLevel.DEBUG, + ) + + val body = client.execute(simpleGetRequest(), async).body() + body.read(ByteArray(5)) + body.close() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |Content-Type: application/json + | + |{"id" + |<-- END HTTP (5-byte body, closed early) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsEndHttpMarkerOnCloseWithoutReading(async: Boolean) { + val responseBody = """{"id":1,"status":"ok"}""" + val headers = Headers.builder().put("Content-Type", "application/json").build() + val client = + loggingClient( + fakeHttpClient( + responseHeaders = headers, + responseBody = responseBody.toByteArray(StandardCharsets.UTF_8), + ), + LogLevel.DEBUG, + ) + + client.execute(simpleGetRequest(), async).body().close() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |Content-Type: application/json + |<-- END HTTP (0-byte body, closed early) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsEndHttpMarkerWhenResponseClosedAfterPartialRead(async: Boolean) { + val responseBody = """{"id":1,"status":"ok"}""" + val headers = Headers.builder().put("Content-Type", "application/json").build() + val client = + loggingClient( + fakeHttpClient( + responseHeaders = headers, + responseBody = responseBody.toByteArray(StandardCharsets.UTF_8), + ), + LogLevel.DEBUG, + ) + + val response = client.execute(simpleGetRequest(), async) + response.body().read(ByteArray(5)) + response.close() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |Content-Type: application/json + | + |{"id" + |<-- END HTTP (5-byte body, closed early) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_doesNotLogEndHttpMarkerWhenResponseClosedWithoutBodyAccess(async: Boolean) { + val responseBody = """{"id":1,"status":"ok"}""" + val headers = Headers.builder().put("Content-Type", "application/json").build() + val client = + loggingClient( + fakeHttpClient( + responseHeaders = headers, + responseBody = responseBody.toByteArray(StandardCharsets.UTF_8), + ), + LogLevel.DEBUG, + ) + + client.execute(simpleGetRequest(), async).close() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |Content-Type: application/json + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun errorLevel_logsRequestFailure(async: Boolean) { + val clock = + clockFrom( + Instant.parse("1998-04-21T00:00:00Z"), + Instant.parse("1998-04-21T00:00:01.234Z"), + ) + val client = + loggingClient( + failingHttpClient(IOException("Connection refused")), + LogLevel.ERROR, + clock, + ) + + assertThatThrownBy { client.execute(simpleGetRequest(), async) } + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- !! IOException: Connection refused (1s 234ms) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun infoLevel_doesNotLogRequestFailure(async: Boolean) { + val client = + loggingClient(failingHttpClient(IOException("Connection refused")), LogLevel.INFO) + + assertThatThrownBy { client.execute(simpleGetRequest(), async) } + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsRequestFailureAfterHeaders(async: Boolean) { + val client = + loggingClient(failingHttpClient(IOException("Connection refused")), LogLevel.DEBUG) + + assertThatThrownBy { client.execute(simpleGetRequest(), async) } + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- !! IOException: Connection refused (0s) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun errorLevel_logsRequestFailureWithoutMessage(async: Boolean) { + val client = loggingClient(failingHttpClient(IOException()), LogLevel.ERROR) + + assertThatThrownBy { client.execute(simpleGetRequest(), async) } + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- !! IOException (0s) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun offLevel_doesNotLogRequestFailure(async: Boolean) { + val client = + loggingClient(failingHttpClient(IOException("Connection refused")), LogLevel.OFF) + + assertThatThrownBy { client.execute(simpleGetRequest(), async) } + + assertThat(stderrOutput()).isEmpty() + } + + @Test + fun errorLevel_logsExecuteAsyncSynchronousThrow() { + val error = IOException("Connection refused") + val client = + loggingClient( + object : HttpClient { + override fun execute( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse = throw UnsupportedOperationException() + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture = throw error + + override fun close() {} + }, + LogLevel.ERROR, + ) + + assertThatThrownBy { client.execute(simpleGetRequest(), async = true) }.isSameAs(error) + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- !! IOException: Connection refused (0s) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun durationFormat_seconds(async: Boolean) { + val clock = + clockFrom( + Instant.parse("1998-04-21T00:00:00Z"), + Instant.parse("1998-04-21T00:00:02.500Z"), + ) + val client = loggingClient(fakeHttpClient(), LogLevel.INFO, clock) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- 200 (2s 500ms) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun durationFormat_minutesAndSeconds(async: Boolean) { + val clock = + clockFrom( + Instant.parse("1998-04-21T00:00:00Z"), + Instant.parse("1998-04-21T00:01:40.467Z"), + ) + val client = loggingClient(fakeHttpClient(), LogLevel.INFO, clock) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- 200 (1m 40s 467ms) + |""" + .trimMargin() + ) + } + + @Test + fun builder_toBuilder_roundtrips() { + val delegate = fakeHttpClient() + val clock = Clock.fixed(Instant.parse("1998-04-21T00:00:00Z"), ZoneOffset.UTC) + val client = + LoggingHttpClient.builder() + .httpClient(delegate) + .level(LogLevel.DEBUG) + .redactedHeaders(setOf("X-Secret")) + .clock(clock) + .build() + + val rebuilt = client.toBuilder().build() + + assertThat(rebuilt.httpClient).isSameAs(delegate) + assertThat(rebuilt.level).isEqualTo(LogLevel.DEBUG) + assertThat(rebuilt.redactedHeaders).containsExactly("X-Secret") + assertThat(rebuilt.clock).isEqualTo(clock) + } + + @Test + fun close_delegatesToUnderlyingClient() { + var closed = false + val delegate = + object : HttpClient { + override fun execute( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse = throw UnsupportedOperationException() + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture = throw UnsupportedOperationException() + + override fun close() { + closed = true + } + } + val client = loggingClient(delegate, LogLevel.OFF) + + client.close() + + assertThat(closed).isTrue() + } + + private fun stderrOutput(): String = errContent.toString("UTF-8") + + private fun loggingClient( + httpClient: HttpClient, + level: LogLevel, + clock: Clock = clockFrom(Instant.parse("1998-04-21T00:00:00Z")), + redactedHeaders: Set = + setOf( + "authorization", + "api-key", + "x-api-key", + "cookie", + "set-cookie", + "x-bb-api-key", + "x-bb-project-id", + "x-model-api-key", + ), + ): LoggingHttpClient = + LoggingHttpClient.builder() + .httpClient(httpClient) + .level(level) + .clock(clock) + .redactedHeaders(redactedHeaders) + .build() + + private fun simpleGetRequest(): HttpRequest = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("v1") + .addPathSegment("resources") + .build() + + private fun postRequestWithBody( + body: String, + contentType: String = "application/json", + contentLength: Long? = null, + ): HttpRequest = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl("https://api.example.com") + .addPathSegment("v1") + .addPathSegment("resources") + .body( + object : HttpRequestBody { + private val bytes = body.toByteArray(StandardCharsets.UTF_8) + + override fun writeTo(outputStream: OutputStream) { + outputStream.write(bytes) + } + + override fun contentType(): String = contentType + + override fun contentLength(): Long = contentLength ?: bytes.size.toLong() + + override fun repeatable(): Boolean = true + + override fun close() {} + } + ) + .build() + + private fun fakeHttpClient( + statusCode: Int = 200, + responseHeaders: Headers = Headers.builder().build(), + responseBody: ByteArray = ByteArray(0), + ): HttpClient = + object : HttpClient { + override fun execute( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse { + // Consume the request body if present to trigger logging. + request.body?.let { + val out = ByteArrayOutputStream() + it.writeTo(out) + } + return fakeResponse(statusCode, responseHeaders, responseBody) + } + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture = + CompletableFuture.completedFuture(execute(request, requestOptions)) + + override fun close() {} + } + + private fun failingHttpClient(error: Throwable): HttpClient = + object : HttpClient { + override fun execute( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse { + request.body?.let { + val out = ByteArrayOutputStream() + it.writeTo(out) + } + throw error + } + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture { + val future = CompletableFuture() + future.completeExceptionally(error) + return future + } + + override fun close() {} + } + + private fun fakeResponse(statusCode: Int, headers: Headers, body: ByteArray): HttpResponse = + object : HttpResponse { + override fun statusCode(): Int = statusCode + + override fun headers(): Headers = headers + + override fun body(): InputStream = ByteArrayInputStream(body) + + override fun close() {} + } + + private fun clockFrom(vararg instants: Instant): Clock = + object : Clock() { + private var index = 0 + + override fun getZone() = ZoneOffset.UTC + + override fun withZone(zone: java.time.ZoneId?) = this + + override fun instant(): Instant { + val instant = instants[index % instants.size] + index++ + return instant + } + } + + private fun HttpClient.execute(request: HttpRequest, async: Boolean): HttpResponse = + if (async) executeAsync(request).get() else execute(request) +} diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/ModelConfigTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/ModelConfigTest.kt index 6e3b3d0..aa99343 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/ModelConfigTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/ModelConfigTest.kt @@ -4,55 +4,187 @@ package com.browserbase.api.models.sessions import com.browserbase.api.core.JsonValue import com.browserbase.api.core.jsonMapper +import com.browserbase.api.errors.StagehandInvalidDataException import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource internal class ModelConfigTest { @Test - fun create() { - val modelConfig = - ModelConfig.builder() + fun ofVertexModelConfigObject() { + val vertexModelConfigObject = + ModelConfig.VertexModelConfigObject.builder() + .auth( + ModelConfig.VertexModelConfigObject.Auth.builder() + .credentials( + ModelConfig.VertexModelConfigObject.Auth.Credentials.builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + ModelConfig.VertexModelConfigObject.Auth.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + ModelConfig.VertexModelConfigObject.ProviderOptions.builder() + .vertex( + ModelConfig.VertexModelConfigObject.ProviderOptions.Vertex.builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + ModelConfig.VertexModelConfigObject.ProviderOptions.Vertex + .Headers + .builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + ModelConfig.VertexModelConfigObject.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() - assertThat(modelConfig.modelName()).isEqualTo("openai/gpt-5.4-mini") - assertThat(modelConfig.apiKey()).contains("sk-some-openai-api-key") - assertThat(modelConfig.baseUrl()).contains("https://api.openai.com/v1") - assertThat(modelConfig.headers()) - .contains( - ModelConfig.Headers.builder() - .putAdditionalProperty("foo", JsonValue.from("string")) - .build() - ) - assertThat(modelConfig.provider()).contains(ModelConfig.Provider.OPENAI) + val modelConfig = ModelConfig.ofVertexModelConfigObject(vertexModelConfigObject) + + assertThat(modelConfig.vertexModelConfigObject()).contains(vertexModelConfigObject) + assertThat(modelConfig.genericModelConfigObject()).isEmpty } @Test - fun roundtrip() { + fun ofVertexModelConfigObjectRoundtrip() { val jsonMapper = jsonMapper() val modelConfig = - ModelConfig.builder() + ModelConfig.ofVertexModelConfigObject( + ModelConfig.VertexModelConfigObject.builder() + .auth( + ModelConfig.VertexModelConfigObject.Auth.builder() + .credentials( + ModelConfig.VertexModelConfigObject.Auth.Credentials.builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + ModelConfig.VertexModelConfigObject.Auth.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) + .modelName("openai/gpt-5.4-mini") + .providerOptions( + ModelConfig.VertexModelConfigObject.ProviderOptions.builder() + .vertex( + ModelConfig.VertexModelConfigObject.ProviderOptions.Vertex.builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + ModelConfig.VertexModelConfigObject.ProviderOptions.Vertex + .Headers + .builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .build() + ) + .build() + ) + .apiKey("sk-some-openai-api-key") + .baseUrl("https://api.openai.com/v1") + .headers( + ModelConfig.VertexModelConfigObject.Headers.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .build() + ) + + val roundtrippedModelConfig = + jsonMapper.readValue( + jsonMapper.writeValueAsString(modelConfig), + jacksonTypeRef(), + ) + + assertThat(roundtrippedModelConfig).isEqualTo(modelConfig) + } + + @Test + fun ofGenericModelConfigObject() { + val genericModelConfigObject = + ModelConfig.GenericModelConfigObject.builder() .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + ModelConfig.GenericModelConfigObject.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) + .provider(ModelConfig.GenericModelConfigObject.Provider.OPENAI) .build() + val modelConfig = ModelConfig.ofGenericModelConfigObject(genericModelConfigObject) + + assertThat(modelConfig.vertexModelConfigObject()).isEmpty + assertThat(modelConfig.genericModelConfigObject()).contains(genericModelConfigObject) + } + + @Test + fun ofGenericModelConfigObjectRoundtrip() { + val jsonMapper = jsonMapper() + val modelConfig = + ModelConfig.ofGenericModelConfigObject( + ModelConfig.GenericModelConfigObject.builder() + .modelName("openai/gpt-5.4-mini") + .apiKey("sk-some-openai-api-key") + .baseUrl("https://api.openai.com/v1") + .headers( + ModelConfig.GenericModelConfigObject.Headers.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .provider(ModelConfig.GenericModelConfigObject.Provider.OPENAI) + .build() + ) + val roundtrippedModelConfig = jsonMapper.readValue( jsonMapper.writeValueAsString(modelConfig), @@ -61,4 +193,21 @@ internal class ModelConfigTest { assertThat(roundtrippedModelConfig).isEqualTo(modelConfig) } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val modelConfig = jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { modelConfig.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } } diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActParamsTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActParamsTest.kt index 31ef59b..186c291 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActParamsTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActParamsTest.kt @@ -19,16 +19,77 @@ internal class SessionActParamsTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionActParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionActParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionActParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionActParams.Options.Model.VertexModelConfigObject.Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) @@ -75,16 +136,78 @@ internal class SessionActParamsTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionActParams.Options.Model.VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionActParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionActParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionActParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionActParams.Options.Model.VertexModelConfigObject.Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) @@ -135,16 +258,78 @@ internal class SessionActParamsTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionActParams.Options.Model.VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionActParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionActParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionActParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionActParams.Options.Model.VertexModelConfigObject.Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) @@ -175,16 +360,77 @@ internal class SessionActParamsTest { .contains( SessionActParams.Options.builder() .model( - ModelConfig.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionActParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionActParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionActParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionActParams.Options.Model.VertexModelConfigObject.Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteParamsTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteParamsTest.kt index da74169..5096ba2 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteParamsTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteParamsTest.kt @@ -18,30 +18,165 @@ internal class SessionExecuteParamsTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.ExecutionModel.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject.builder() + .auth( + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) @@ -99,30 +234,166 @@ internal class SessionExecuteParamsTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.ExecutionModel.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject.builder() + .auth( + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) @@ -184,30 +455,166 @@ internal class SessionExecuteParamsTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.ExecutionModel.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject.builder() + .auth( + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) @@ -241,30 +648,165 @@ internal class SessionExecuteParamsTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.ExecutionModel.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject.builder() + .auth( + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractParamsTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractParamsTest.kt index 30d7652..0ab77e4 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractParamsTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractParamsTest.kt @@ -20,18 +20,81 @@ internal class SessionExtractParamsTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionExtractParams.Options.Model.VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExtractParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExtractParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) + .screenshot(false) .selector("#main-content") .timeout(30000.0) .build() @@ -66,18 +129,84 @@ internal class SessionExtractParamsTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExtractParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) + .screenshot(false) .selector("#main-content") .timeout(30000.0) .build() @@ -116,18 +245,84 @@ internal class SessionExtractParamsTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExtractParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) + .screenshot(false) .selector("#main-content") .timeout(30000.0) .build() @@ -149,18 +344,81 @@ internal class SessionExtractParamsTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionExtractParams.Options.Model.VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExtractParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExtractParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) + .screenshot(false) .selector("#main-content") .timeout(30000.0) .build() diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveParamsTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveParamsTest.kt index 08f0c13..9d88897 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveParamsTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveParamsTest.kt @@ -18,17 +18,80 @@ internal class SessionObserveParamsTest { .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionObserveParams.Options.Model.VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionObserveParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionObserveParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .selector("nav") @@ -72,17 +135,83 @@ internal class SessionObserveParamsTest { .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionObserveParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .selector("nav") @@ -130,17 +259,83 @@ internal class SessionObserveParamsTest { .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionObserveParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .selector("nav") @@ -170,17 +365,80 @@ internal class SessionObserveParamsTest { assertThat(body.options()) .contains( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionObserveParams.Options.Model.VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionObserveParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionObserveParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .selector("nav") diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/ServiceParamsTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/ServiceParamsTest.kt index 392cc05..bf61d8e 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/ServiceParamsTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/ServiceParamsTest.kt @@ -5,7 +5,6 @@ package com.browserbase.api.services import com.browserbase.api.client.StagehandClient import com.browserbase.api.client.okhttp.StagehandOkHttpClient import com.browserbase.api.core.JsonValue -import com.browserbase.api.models.sessions.ModelConfig import com.browserbase.api.models.sessions.SessionActParams import com.browserbase.api.models.sessions.SessionStartParams import com.github.tomakehurst.wiremock.client.WireMock.anyUrl @@ -234,16 +233,78 @@ internal class ServiceParamsTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionActParams.Options.Model.VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionActParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionActParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionActParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionActParams.Options.Model.VertexModelConfigObject.Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/async/SessionServiceAsyncTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/async/SessionServiceAsyncTest.kt index 8d708e4..14df84e 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/async/SessionServiceAsyncTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/async/SessionServiceAsyncTest.kt @@ -4,7 +4,6 @@ package com.browserbase.api.services.async import com.browserbase.api.client.okhttp.StagehandOkHttpClientAsync import com.browserbase.api.core.JsonValue -import com.browserbase.api.models.sessions.ModelConfig import com.browserbase.api.models.sessions.SessionActParams import com.browserbase.api.models.sessions.SessionEndParams import com.browserbase.api.models.sessions.SessionExecuteParams @@ -39,16 +38,81 @@ internal class SessionServiceAsyncTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionActParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionActParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionActParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionActParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) @@ -96,16 +160,81 @@ internal class SessionServiceAsyncTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionActParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionActParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionActParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionActParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) @@ -176,30 +305,173 @@ internal class SessionServiceAsyncTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig + .ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig + .ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) @@ -251,30 +523,173 @@ internal class SessionServiceAsyncTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig + .ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig + .ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) @@ -329,18 +744,85 @@ internal class SessionServiceAsyncTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExtractParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) + .screenshot(false) .selector("#main-content") .timeout(30000.0) .build() @@ -379,18 +861,85 @@ internal class SessionServiceAsyncTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExtractParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) + .screenshot(false) .selector("#main-content") .timeout(30000.0) .build() @@ -461,17 +1010,84 @@ internal class SessionServiceAsyncTest { .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionObserveParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .selector("nav") @@ -519,17 +1135,84 @@ internal class SessionServiceAsyncTest { .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionObserveParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .selector("nav") diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/blocking/SessionServiceTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/blocking/SessionServiceTest.kt index 7ede1f2..ca3e009 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/blocking/SessionServiceTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/blocking/SessionServiceTest.kt @@ -4,7 +4,6 @@ package com.browserbase.api.services.blocking import com.browserbase.api.client.okhttp.StagehandOkHttpClient import com.browserbase.api.core.JsonValue -import com.browserbase.api.models.sessions.ModelConfig import com.browserbase.api.models.sessions.SessionActParams import com.browserbase.api.models.sessions.SessionEndParams import com.browserbase.api.models.sessions.SessionExecuteParams @@ -39,16 +38,81 @@ internal class SessionServiceTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionActParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionActParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionActParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionActParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) @@ -95,16 +159,81 @@ internal class SessionServiceTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() + .credentials( + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionActParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionActParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionActParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionActParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) @@ -174,30 +303,173 @@ internal class SessionServiceTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig + .ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig + .ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) @@ -248,30 +520,173 @@ internal class SessionServiceTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig + .ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig + .ExecutionModel + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) @@ -326,18 +741,85 @@ internal class SessionServiceTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExtractParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) + .screenshot(false) .selector("#main-content") .timeout(30000.0) .build() @@ -375,18 +857,85 @@ internal class SessionServiceTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionExtractParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionExtractParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) + .screenshot(false) .selector("#main-content") .timeout(30000.0) .build() @@ -456,17 +1005,84 @@ internal class SessionServiceTest { .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionObserveParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .selector("nav") @@ -513,17 +1129,84 @@ internal class SessionServiceTest { .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject + .Auth + .builder() + .credentials( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .modelName("openai/gpt-5.4-mini") + .providerOptions( + SessionObserveParams.Options.Model.VertexModelConfigObject + .ProviderOptions + .builder() + .vertex( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + SessionObserveParams.Options.Model + .VertexModelConfigObject + .ProviderOptions + .Vertex + .Headers + .builder() + .putAdditionalProperty( + "foo", + JsonValue.from("string"), + ) + .build() + ) + .build() + ) + .build() + ) .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") .headers( - ModelConfig.Headers.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject + .Headers + .builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .provider(ModelConfig.Provider.OPENAI) .build() ) .selector("nav") diff --git a/stagehand-java-example/src/main/java/com/stagehand/api/example/LocalBrowserPlaywrightExample.java b/stagehand-java-example/src/main/java/com/stagehand/api/example/LocalBrowserPlaywrightExample.java index 5745ba8..78c50d3 100644 --- a/stagehand-java-example/src/main/java/com/stagehand/api/example/LocalBrowserPlaywrightExample.java +++ b/stagehand-java-example/src/main/java/com/stagehand/api/example/LocalBrowserPlaywrightExample.java @@ -4,7 +4,6 @@ import com.browserbase.api.client.okhttp.StagehandOkHttpClient; import com.browserbase.api.core.JsonValue; import com.browserbase.api.core.http.StreamResponse; -import com.browserbase.api.models.sessions.ModelConfig; import com.browserbase.api.models.sessions.SessionActParams; import com.browserbase.api.models.sessions.SessionEndParams; import com.browserbase.api.models.sessions.SessionExecuteParams; @@ -104,7 +103,7 @@ public static void main(String[] args) { .maxSteps(3.0) .build()) .agentConfig(SessionExecuteParams.AgentConfig.builder() - .model(ModelConfig.builder() + .model(SessionExecuteParams.AgentConfig.Model.GenericModelConfigObject.builder() .modelName("anthropic/claude-opus-4-6") .apiKey(System.getProperty("stagehand.modelApiKey")) .build()) diff --git a/stagehand-java-example/src/main/java/com/stagehand/api/example/LocalServerMultiregionBrowserExample.java b/stagehand-java-example/src/main/java/com/stagehand/api/example/LocalServerMultiregionBrowserExample.java index 16fd19b..851a10e 100644 --- a/stagehand-java-example/src/main/java/com/stagehand/api/example/LocalServerMultiregionBrowserExample.java +++ b/stagehand-java-example/src/main/java/com/stagehand/api/example/LocalServerMultiregionBrowserExample.java @@ -5,7 +5,6 @@ import com.browserbase.api.core.JsonValue; import com.browserbase.api.core.RequestOptions; import com.browserbase.api.core.http.StreamResponse; -import com.browserbase.api.models.sessions.ModelConfig; import com.browserbase.api.models.sessions.SessionActParams; import com.browserbase.api.models.sessions.SessionEndParams; import com.browserbase.api.models.sessions.SessionExecuteParams; @@ -121,7 +120,7 @@ public static void main(String[] args) { .maxSteps(15.0) .build()) .agentConfig(SessionExecuteParams.AgentConfig.builder() - .model(ModelConfig.builder() + .model(SessionExecuteParams.AgentConfig.Model.GenericModelConfigObject.builder() .modelName("anthropic/claude-opus-4-6") .apiKey(System.getProperty("stagehand.modelApiKey")) .build()) diff --git a/stagehand-java-example/src/main/java/com/stagehand/api/example/Main.java b/stagehand-java-example/src/main/java/com/stagehand/api/example/Main.java index 29692cd..3c6f087 100644 --- a/stagehand-java-example/src/main/java/com/stagehand/api/example/Main.java +++ b/stagehand-java-example/src/main/java/com/stagehand/api/example/Main.java @@ -136,10 +136,12 @@ public static void main(String[] args) { .maxSteps(15.0) .build()) .agentConfig(SessionExecuteParams.AgentConfig.builder() - .model(ModelConfig.builder() - .modelName("anthropic/claude-opus-4-6") - .apiKey(System.getProperty("stagehand.modelApiKey")) - .build()) + .model( + SessionExecuteParams.AgentConfig.Model.GenericModelConfigObject + .builder() + .modelName("anthropic/claude-opus-4-6") + .apiKey(System.getProperty("stagehand.modelApiKey")) + .build()) .cua(false) .build()) .build(), diff --git a/stagehand-java-example/src/main/java/com/stagehand/api/example/RemoteBrowserPlaywrightExample.java b/stagehand-java-example/src/main/java/com/stagehand/api/example/RemoteBrowserPlaywrightExample.java index bfc9bc6..25d59aa 100644 --- a/stagehand-java-example/src/main/java/com/stagehand/api/example/RemoteBrowserPlaywrightExample.java +++ b/stagehand-java-example/src/main/java/com/stagehand/api/example/RemoteBrowserPlaywrightExample.java @@ -5,7 +5,6 @@ import com.browserbase.api.core.JsonValue; import com.browserbase.api.core.RequestOptions; import com.browserbase.api.core.http.StreamResponse; -import com.browserbase.api.models.sessions.ModelConfig; import com.browserbase.api.models.sessions.SessionActParams; import com.browserbase.api.models.sessions.SessionEndParams; import com.browserbase.api.models.sessions.SessionExecuteParams; @@ -125,7 +124,7 @@ public static void main(String[] args) { .maxSteps(15.0) .build()) .agentConfig(SessionExecuteParams.AgentConfig.builder() - .model(ModelConfig.builder() + .model(SessionExecuteParams.AgentConfig.Model.GenericModelConfigObject.builder() .modelName("anthropic/claude-opus-4-6") .apiKey(System.getProperty("stagehand.modelApiKey")) .build()) diff --git a/stagehand-java-proguard-test/src/test/kotlin/com/browserbase/api/proguard/ProGuardCompatibilityTest.kt b/stagehand-java-proguard-test/src/test/kotlin/com/browserbase/api/proguard/ProGuardCompatibilityTest.kt index 1099052..f42a286 100644 --- a/stagehand-java-proguard-test/src/test/kotlin/com/browserbase/api/proguard/ProGuardCompatibilityTest.kt +++ b/stagehand-java-proguard-test/src/test/kotlin/com/browserbase/api/proguard/ProGuardCompatibilityTest.kt @@ -3,8 +3,10 @@ package com.browserbase.api.proguard import com.browserbase.api.client.okhttp.StagehandOkHttpClient +import com.browserbase.api.core.JsonValue import com.browserbase.api.core.jsonMapper import com.browserbase.api.models.sessions.Action +import com.browserbase.api.models.sessions.ModelConfig import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import kotlin.reflect.full.memberFunctions import kotlin.reflect.jvm.javaMethod @@ -72,4 +74,73 @@ internal class ProGuardCompatibilityTest { assertThat(roundtrippedAction).isEqualTo(action) } + + @Test + fun modelConfigRoundtrip() { + val jsonMapper = jsonMapper() + val modelConfig = + ModelConfig.ofVertexModelConfigObject( + ModelConfig.VertexModelConfigObject.builder() + .auth( + ModelConfig.VertexModelConfigObject.Auth.builder() + .credentials( + ModelConfig.VertexModelConfigObject.Auth.Credentials.builder() + .clientEmail("client_email") + .privateKey("private_key") + .authProviderX509CertUrl("https://example.com") + .authUri("https://example.com") + .clientId("client_id") + .clientX509CertUrl("https://example.com") + .privateKeyId("private_key_id") + .projectId("project_id") + .tokenUri("https://example.com") + .type( + ModelConfig.VertexModelConfigObject.Auth.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) + .modelName("openai/gpt-5.4-mini") + .providerOptions( + ModelConfig.VertexModelConfigObject.ProviderOptions.builder() + .vertex( + ModelConfig.VertexModelConfigObject.ProviderOptions.Vertex.builder() + .location("us-central1") + .project("my-gcp-project") + .baseUrl("https://example.com") + .headers( + ModelConfig.VertexModelConfigObject.ProviderOptions.Vertex + .Headers + .builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .build() + ) + .build() + ) + .apiKey("sk-some-openai-api-key") + .baseUrl("https://api.openai.com/v1") + .headers( + ModelConfig.VertexModelConfigObject.Headers.builder() + .putAdditionalProperty("foo", JsonValue.from("string")) + .build() + ) + .build() + ) + + val roundtrippedModelConfig = + jsonMapper.readValue( + jsonMapper.writeValueAsString(modelConfig), + jacksonTypeRef(), + ) + + assertThat(roundtrippedModelConfig).isEqualTo(modelConfig) + } }