From 1d845661917baf3d12a97286cf760e0d0fda0218 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 29 May 2026 15:26:43 +0000 Subject: [PATCH 01/11] feat(client): improve logging Logging is now: 1. Streaming 4. Configurable in-memory 5. Generally more robust 6. Usable with any underlying http client --- README.md | 15 +- stagehand-java-client-okhttp/build.gradle.kts | 1 - .../api/client/okhttp/OkHttpClient.kt | 16 - .../client/okhttp/StagehandOkHttpClient.kt | 10 + .../okhttp/StagehandOkHttpClientAsync.kt | 10 + .../com/browserbase/api/core/ClientOptions.kt | 48 +- .../com/browserbase/api/core/LogLevel.kt | 33 + .../kotlin/com/browserbase/api/core/Utils.kt | 6 + .../api/core/http/LoggingHttpClient.kt | 628 +++++++++++ .../api/core/http/LoggingHttpClientTest.kt | 999 ++++++++++++++++++ 10 files changed, 1739 insertions(+), 27 deletions(-) create mode 100644 stagehand-java-core/src/main/kotlin/com/browserbase/api/core/LogLevel.kt create mode 100644 stagehand-java-core/src/main/kotlin/com/browserbase/api/core/http/LoggingHttpClient.kt create mode 100644 stagehand-java-core/src/test/kotlin/com/browserbase/api/core/http/LoggingHttpClientTest.kt diff --git a/README.md b/README.md index 86c046b..7d3d74e 100644 --- a/README.md +++ b/README.md @@ -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/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..9952373 --- /dev/null +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/http/LoggingHttpClient.kt @@ -0,0 +1,628 @@ +// 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("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("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("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/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..b43c5d0 --- /dev/null +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/core/http/LoggingHttpClientTest.kt @@ -0,0 +1,999 @@ +// 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("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) +} From ad7e53b2d680596a2d0f9e7e41d92265d18b4e9a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 17:46:11 +0000 Subject: [PATCH 02/11] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 391cde3..57aa7a5 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-e629569417ad17cad5c73180109b4c3ae778f38063fc72146fa82f82de145911.yml +openapi_spec_hash: 42e4eedbc0fcc772bb271191a067bce1 config_hash: 1fb12ae9b478488bc1e56bfbdc210b01 From bbbc501e6d32fe8e2eaf9b661e14f506cbcb9c2a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 18:23:01 +0000 Subject: [PATCH 03/11] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 57aa7a5..094ac49 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-e629569417ad17cad5c73180109b4c3ae778f38063fc72146fa82f82de145911.yml -openapi_spec_hash: 42e4eedbc0fcc772bb271191a067bce1 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-49b40c7425adba9e67fc102838c5216c45ca1f7ef4c10823c5665fd413538504.yml +openapi_spec_hash: 6880dc029df2e88dfe8943c0dec5a3a5 config_hash: 1fb12ae9b478488bc1e56bfbdc210b01 From aa7ad0c2033478107f3944b625d1b172b8f39e83 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 18:25:46 +0000 Subject: [PATCH 04/11] feat: [feat]: add `ignoreSelectors` to `observe()` --- .stats.yml | 4 +- .../models/sessions/SessionObserveParams.kt | 73 +++++++++++++++++-- .../sessions/SessionObserveParamsTest.kt | 4 + .../services/async/SessionServiceAsyncTest.kt | 2 + .../services/blocking/SessionServiceTest.kt | 2 + 5 files changed, 78 insertions(+), 7 deletions(-) diff --git a/.stats.yml b/.stats.yml index 094ac49..0339c57 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-49b40c7425adba9e67fc102838c5216c45ca1f7ef4c10823c5665fd413538504.yml -openapi_spec_hash: 6880dc029df2e88dfe8943c0dec5a3a5 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-eae8400fade7b2c8329c4148f56de92e147c34c0feecb420c015aab6544a9acc.yml +openapi_spec_hash: 0a9eff1ac1d464e89cbd9db64709b08a config_hash: 1fb12ae9b478488bc1e56bfbdc210b01 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..dd8ffaa 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,7 @@ 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.getOrThrow import com.browserbase.api.core.http.Headers import com.browserbase.api.core.http.QueryParams @@ -573,6 +574,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 +584,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 +595,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 +640,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 +701,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 +710,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 +718,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)) @@ -775,7 +828,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 +854,7 @@ private constructor( return@apply } + ignoreSelectors() model().ifPresent { it.validate() } selector() timeout() @@ -817,7 +878,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) @@ -1157,6 +1219,7 @@ private constructor( } return other is Options && + ignoreSelectors == other.ignoreSelectors && model == other.model && selector == other.selector && timeout == other.timeout && @@ -1165,13 +1228,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/models/sessions/SessionObserveParamsTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveParamsTest.kt index 08f0c13..df6c3f7 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,6 +18,7 @@ internal class SessionObserveParamsTest { .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( ModelConfig.builder() .modelName("openai/gpt-5.4-mini") @@ -72,6 +73,7 @@ internal class SessionObserveParamsTest { .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( ModelConfig.builder() .modelName("openai/gpt-5.4-mini") @@ -130,6 +132,7 @@ internal class SessionObserveParamsTest { .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( ModelConfig.builder() .modelName("openai/gpt-5.4-mini") @@ -170,6 +173,7 @@ internal class SessionObserveParamsTest { assertThat(body.options()) .contains( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( ModelConfig.builder() .modelName("openai/gpt-5.4-mini") 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..369528c 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 @@ -461,6 +461,7 @@ internal class SessionServiceAsyncTest { .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( ModelConfig.builder() .modelName("openai/gpt-5.4-mini") @@ -519,6 +520,7 @@ internal class SessionServiceAsyncTest { .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( ModelConfig.builder() .modelName("openai/gpt-5.4-mini") 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..2cea0fd 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 @@ -456,6 +456,7 @@ internal class SessionServiceTest { .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( ModelConfig.builder() .modelName("openai/gpt-5.4-mini") @@ -513,6 +514,7 @@ internal class SessionServiceTest { .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() + .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( ModelConfig.builder() .modelName("openai/gpt-5.4-mini") From ce7e1d278d5f7a9ceda6bc3108ed8d097f9b313d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 8 May 2026 02:01:14 +0000 Subject: [PATCH 05/11] chore: redact api-key headers in debug logs --- .../api/core/http/LoggingHttpClient.kt | 17 ++++++++++++++--- .../api/core/http/LoggingHttpClientTest.kt | 12 +++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) 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 index 9952373..564bbfc 100644 --- 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 @@ -31,7 +31,8 @@ private constructor( /** * Sensitive headers to redact from logs. * - * Defaults to `Set.of("x-bb-api-key", "x-bb-project-id", "x-model-api-key")`. + * 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, /** @@ -193,7 +194,16 @@ private constructor( private var httpClient: HttpClient? = null private var redactedHeaders: Set = - setOf("x-bb-api-key", "x-bb-project-id", "x-model-api-key") + 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 @@ -211,7 +221,8 @@ private constructor( /** * Sensitive headers to redact from logs. * - * Defaults to `Set.of("x-bb-api-key", "x-bb-project-id", "x-model-api-key")`. + * 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 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 index b43c5d0..a35c99d 100644 --- 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 @@ -870,7 +870,17 @@ internal class LoggingHttpClientTest { httpClient: HttpClient, level: LogLevel, clock: Clock = clockFrom(Instant.parse("1998-04-21T00:00:00Z")), - redactedHeaders: Set = setOf("x-bb-api-key", "x-bb-project-id", "x-model-api-key"), + 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) From 0a43c2c0eeca9ea2d9842bbb6e9637481c96b007 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 02:00:40 +0000 Subject: [PATCH 06/11] ci: pin GitHub Actions to commit SHAs Pin all GitHub Actions referenced in generated workflows (both first-party `actions/*` and third-party) to immutable commit SHAs. Updating pinned actions is now a deliberate codegen-side bump rather than implicit on every workflow run. --- .github/workflows/ci.yml | 20 ++++++++++---------- .github/workflows/publish-sonatype.yml | 6 +++--- .github/workflows/release-doctor.yml | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) 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: | From f072067e7adef04886423e1232e6b7e42e5f8a28 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 19:16:18 +0000 Subject: [PATCH 07/11] feat: STG-1756 add Vertex auth params to Stagehand spec --- .stats.yml | 4 +- .../api/models/sessions/ModelConfig.kt | 1370 ++++++++++++++++- .../api/models/sessions/ModelConfigTest.kt | 77 + .../models/sessions/SessionActParamsTest.kt | 108 ++ .../sessions/SessionExecuteParamsTest.kt | 216 +++ .../sessions/SessionExtractParamsTest.kt | 108 ++ .../sessions/SessionObserveParamsTest.kt | 108 ++ .../api/services/ServiceParamsTest.kt | 27 + .../services/async/SessionServiceAsyncTest.kt | 280 ++++ .../services/blocking/SessionServiceTest.kt | 280 ++++ 10 files changed, 2573 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0339c57..220445b 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-eae8400fade7b2c8329c4148f56de92e147c34c0feecb420c015aab6544a9acc.yml -openapi_spec_hash: 0a9eff1ac1d464e89cbd9db64709b08a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-e77d6b15f0a94b16a54ef87a84d2cabe49eb11cff5ceba76f00dd788ff483eab.yml +openapi_spec_hash: a1dab7fe72a772d188a15305124ebd73 config_hash: 1fb12ae9b478488bc1e56bfbdc210b01 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..da089be 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,18 +2,29 @@ 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 @@ -25,7 +36,10 @@ private constructor( private val modelName: JsonField, private val apiKey: JsonField, private val baseUrl: JsonField, + private val googleAuthOptions: JsonField, private val headers: JsonField, + private val location: JsonField, + private val project: JsonField, private val provider: JsonField, private val additionalProperties: MutableMap, ) { @@ -35,9 +49,24 @@ private constructor( @JsonProperty("modelName") @ExcludeMissing modelName: JsonField = JsonMissing.of(), @JsonProperty("apiKey") @ExcludeMissing apiKey: JsonField = JsonMissing.of(), @JsonProperty("baseURL") @ExcludeMissing baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("googleAuthOptions") + @ExcludeMissing + googleAuthOptions: JsonField = JsonMissing.of(), @JsonProperty("headers") @ExcludeMissing headers: JsonField = JsonMissing.of(), + @JsonProperty("location") @ExcludeMissing location: JsonField = JsonMissing.of(), + @JsonProperty("project") @ExcludeMissing project: JsonField = JsonMissing.of(), @JsonProperty("provider") @ExcludeMissing provider: JsonField = JsonMissing.of(), - ) : this(modelName, apiKey, baseUrl, headers, provider, mutableMapOf()) + ) : this( + modelName, + apiKey, + baseUrl, + googleAuthOptions, + headers, + location, + project, + provider, + mutableMapOf(), + ) /** * Model name string with provider prefix (e.g., 'openai/gpt-5-nano') @@ -63,6 +92,15 @@ private constructor( */ fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + /** + * google-auth-library options used to authenticate Vertex AI models + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun googleAuthOptions(): Optional = + googleAuthOptions.getOptional("googleAuthOptions") + /** * Custom headers sent with every request to the model provider * @@ -71,6 +109,22 @@ private constructor( */ fun headers(): Optional = headers.getOptional("headers") + /** + * Google Cloud location for Vertex AI models + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun location(): Optional = location.getOptional("location") + + /** + * Google Cloud project ID for Vertex AI models + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun project(): Optional = project.getOptional("project") + /** * AI provider for the model (or provide a baseURL endpoint instead) * @@ -100,6 +154,16 @@ private constructor( */ @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + /** + * Returns the raw JSON value of [googleAuthOptions]. + * + * Unlike [googleAuthOptions], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("googleAuthOptions") + @ExcludeMissing + fun _googleAuthOptions(): JsonField = googleAuthOptions + /** * Returns the raw JSON value of [headers]. * @@ -107,6 +171,20 @@ private constructor( */ @JsonProperty("headers") @ExcludeMissing fun _headers(): JsonField = 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 [provider]. * @@ -145,7 +223,10 @@ private constructor( private var modelName: JsonField? = null private var apiKey: JsonField = JsonMissing.of() private var baseUrl: JsonField = JsonMissing.of() + private var googleAuthOptions: JsonField = JsonMissing.of() private var headers: JsonField = JsonMissing.of() + private var location: JsonField = JsonMissing.of() + private var project: JsonField = JsonMissing.of() private var provider: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -154,7 +235,10 @@ private constructor( modelName = modelConfig.modelName apiKey = modelConfig.apiKey baseUrl = modelConfig.baseUrl + googleAuthOptions = modelConfig.googleAuthOptions headers = modelConfig.headers + location = modelConfig.location + project = modelConfig.project provider = modelConfig.provider additionalProperties = modelConfig.additionalProperties.toMutableMap() } @@ -193,6 +277,21 @@ private constructor( */ fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl } + /** google-auth-library options used to authenticate Vertex AI models */ + fun googleAuthOptions(googleAuthOptions: GoogleAuthOptions) = + googleAuthOptions(JsonField.of(googleAuthOptions)) + + /** + * Sets [Builder.googleAuthOptions] to an arbitrary JSON value. + * + * You should usually call [Builder.googleAuthOptions] with a well-typed [GoogleAuthOptions] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun googleAuthOptions(googleAuthOptions: JsonField) = apply { + this.googleAuthOptions = googleAuthOptions + } + /** Custom headers sent with every request to the model provider */ fun headers(headers: Headers) = headers(JsonField.of(headers)) @@ -204,6 +303,28 @@ private constructor( */ fun headers(headers: JsonField) = apply { this.headers = headers } + /** 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 } + /** AI provider for the model (or provide a baseURL endpoint instead) */ fun provider(provider: Provider) = provider(JsonField.of(provider)) @@ -252,7 +373,10 @@ private constructor( checkRequired("modelName", modelName), apiKey, baseUrl, + googleAuthOptions, headers, + location, + project, provider, additionalProperties.toMutableMap(), ) @@ -276,7 +400,10 @@ private constructor( modelName() apiKey() baseUrl() + googleAuthOptions().ifPresent { it.validate() } headers().ifPresent { it.validate() } + location() + project() provider().ifPresent { it.validate() } validated = true } @@ -299,9 +426,1227 @@ private constructor( (if (modelName.asKnown().isPresent) 1 else 0) + (if (apiKey.asKnown().isPresent) 1 else 0) + (if (baseUrl.asKnown().isPresent) 1 else 0) + + (googleAuthOptions.asKnown().getOrNull()?.validity() ?: 0) + (headers.asKnown().getOrNull()?.validity() ?: 0) + + (if (location.asKnown().isPresent) 1 else 0) + + (if (project.asKnown().isPresent) 1 else 0) + (provider.asKnown().getOrNull()?.validity() ?: 0) + /** google-auth-library options used to authenticate Vertex AI models */ + class GoogleAuthOptions + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val credentials: JsonField, + 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("projectId") + @ExcludeMissing + projectId: JsonField = JsonMissing.of(), + @JsonProperty("scopes") @ExcludeMissing scopes: JsonField = JsonMissing.of(), + @JsonProperty("universeDomain") + @ExcludeMissing + universeDomain: JsonField = JsonMissing.of(), + ) : this(credentials, projectId, scopes, universeDomain, mutableMapOf()) + + /** + * Google Cloud service account credentials + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun credentials(): Optional = credentials.getOptional("credentials") + + /** + * 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 [GoogleAuthOptions]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [GoogleAuthOptions]. */ + class Builder internal constructor() { + + private var credentials: JsonField = JsonMissing.of() + 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(googleAuthOptions: GoogleAuthOptions) = apply { + credentials = googleAuthOptions.credentials + projectId = googleAuthOptions.projectId + scopes = googleAuthOptions.scopes + universeDomain = googleAuthOptions.universeDomain + additionalProperties = googleAuthOptions.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 + } + + /** 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 [GoogleAuthOptions]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): GoogleAuthOptions = + GoogleAuthOptions( + credentials, + 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(): GoogleAuthOptions = apply { + if (validated) { + return@apply + } + + credentials().ifPresent { it.validate() } + 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) + + (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 GoogleAuthOptions && + credentials == other.credentials && + projectId == other.projectId && + scopes == other.scopes && + universeDomain == other.universeDomain && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(credentials, projectId, scopes, universeDomain, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "GoogleAuthOptions{credentials=$credentials, projectId=$projectId, scopes=$scopes, universeDomain=$universeDomain, additionalProperties=$additionalProperties}" + } + /** Custom headers sent with every request to the model provider */ class Headers @JsonCreator @@ -436,6 +1781,8 @@ private constructor( @JvmField val BEDROCK = of("bedrock") + @JvmField val VERTEX = of("vertex") + @JvmStatic fun of(value: String) = Provider(JsonField.of(value)) } @@ -446,6 +1793,7 @@ private constructor( GOOGLE, MICROSOFT, BEDROCK, + VERTEX, } /** @@ -463,6 +1811,7 @@ private constructor( GOOGLE, MICROSOFT, BEDROCK, + VERTEX, /** An enum member indicating that [Provider] was instantiated with an unknown value. */ _UNKNOWN, } @@ -481,6 +1830,7 @@ private constructor( GOOGLE -> Value.GOOGLE MICROSOFT -> Value.MICROSOFT BEDROCK -> Value.BEDROCK + VERTEX -> Value.VERTEX else -> Value._UNKNOWN } @@ -500,6 +1850,7 @@ private constructor( GOOGLE -> Known.GOOGLE MICROSOFT -> Known.MICROSOFT BEDROCK -> Known.BEDROCK + VERTEX -> Known.VERTEX else -> throw StagehandInvalidDataException("Unknown Provider: $value") } @@ -575,17 +1926,30 @@ private constructor( modelName == other.modelName && apiKey == other.apiKey && baseUrl == other.baseUrl && + googleAuthOptions == other.googleAuthOptions && headers == other.headers && + location == other.location && + project == other.project && provider == other.provider && additionalProperties == other.additionalProperties } private val hashCode: Int by lazy { - Objects.hash(modelName, apiKey, baseUrl, headers, provider, additionalProperties) + Objects.hash( + modelName, + apiKey, + baseUrl, + googleAuthOptions, + headers, + location, + project, + provider, + additionalProperties, + ) } override fun hashCode(): Int = hashCode override fun toString() = - "ModelConfig{modelName=$modelName, apiKey=$apiKey, baseUrl=$baseUrl, headers=$headers, provider=$provider, additionalProperties=$additionalProperties}" + "ModelConfig{modelName=$modelName, apiKey=$apiKey, baseUrl=$baseUrl, googleAuthOptions=$googleAuthOptions, headers=$headers, location=$location, project=$project, provider=$provider, additionalProperties=$additionalProperties}" } 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..6005d44 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 @@ -17,23 +17,74 @@ internal class ModelConfigTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type.SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .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.googleAuthOptions()) + .contains( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type.SERVICE_ACCOUNT) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) assertThat(modelConfig.headers()) .contains( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + assertThat(modelConfig.location()).contains("us-central1") + assertThat(modelConfig.project()).contains("my-gcp-project") assertThat(modelConfig.provider()).contains(ModelConfig.Provider.OPENAI) } @@ -45,11 +96,37 @@ internal class ModelConfigTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type.SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() 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..2d38cf9 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 @@ -23,11 +23,38 @@ internal class SessionActParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -79,11 +106,38 @@ internal class SessionActParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -139,11 +193,38 @@ internal class SessionActParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -179,11 +260,38 @@ internal class SessionActParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) 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..351b711 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 @@ -22,11 +22,38 @@ internal class SessionExecuteParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -36,11 +63,38 @@ internal class SessionExecuteParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -103,11 +157,38 @@ internal class SessionExecuteParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -117,11 +198,38 @@ internal class SessionExecuteParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -188,11 +296,38 @@ internal class SessionExecuteParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -202,11 +337,38 @@ internal class SessionExecuteParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -245,11 +407,38 @@ internal class SessionExecuteParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -259,11 +448,38 @@ internal class SessionExecuteParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) 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..7ca3456 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 @@ -24,11 +24,38 @@ internal class SessionExtractParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -70,11 +97,38 @@ internal class SessionExtractParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -120,11 +174,38 @@ internal class SessionExtractParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -153,11 +234,38 @@ internal class SessionExtractParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .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 df6c3f7..3c7b1c1 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 @@ -24,11 +24,38 @@ internal class SessionObserveParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -79,11 +106,38 @@ internal class SessionObserveParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -138,11 +192,38 @@ internal class SessionObserveParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -179,11 +260,38 @@ internal class SessionObserveParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) 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..f36435f 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 @@ -238,11 +238,38 @@ internal class ServiceParamsTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) 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 369528c..81b000b 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 @@ -43,11 +43,39 @@ internal class SessionServiceAsyncTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -100,11 +128,39 @@ internal class SessionServiceAsyncTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -180,11 +236,39 @@ internal class SessionServiceAsyncTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -194,11 +278,39 @@ internal class SessionServiceAsyncTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -255,11 +367,39 @@ internal class SessionServiceAsyncTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -269,11 +409,39 @@ internal class SessionServiceAsyncTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -333,11 +501,39 @@ internal class SessionServiceAsyncTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -383,11 +579,39 @@ internal class SessionServiceAsyncTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -467,11 +691,39 @@ internal class SessionServiceAsyncTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -526,11 +778,39 @@ internal class SessionServiceAsyncTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) 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 2cea0fd..1279ff4 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 @@ -43,11 +43,39 @@ internal class SessionServiceTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -99,11 +127,39 @@ internal class SessionServiceTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -178,11 +234,39 @@ internal class SessionServiceTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -192,11 +276,39 @@ internal class SessionServiceTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -252,11 +364,39 @@ internal class SessionServiceTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -266,11 +406,39 @@ internal class SessionServiceTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -330,11 +498,39 @@ internal class SessionServiceTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -379,11 +575,39 @@ internal class SessionServiceTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -462,11 +686,39 @@ internal class SessionServiceTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) @@ -520,11 +772,39 @@ internal class SessionServiceTest { .modelName("openai/gpt-5.4-mini") .apiKey("sk-some-openai-api-key") .baseUrl("https://api.openai.com/v1") + .googleAuthOptions( + ModelConfig.GoogleAuthOptions.builder() + .credentials( + ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials + .Type + .SERVICE_ACCOUNT + ) + .universeDomain("universe_domain") + .build() + ) + .projectId("projectId") + .scopes("string") + .universeDomain("universeDomain") + .build() + ) .headers( ModelConfig.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .location("us-central1") + .project("my-gcp-project") .provider(ModelConfig.Provider.OPENAI) .build() ) From 043d138a98a8caab6bc2c59c2abb0a864019c3c5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 21:00:21 +0000 Subject: [PATCH 08/11] feat: Add `screenshot` option to Extract --- .stats.yml | 4 +- .../models/sessions/SessionExtractParams.kt | 56 ++++++++++++++++++- .../sessions/SessionExtractParamsTest.kt | 4 ++ .../services/async/SessionServiceAsyncTest.kt | 2 + .../services/blocking/SessionServiceTest.kt | 2 + 5 files changed, 63 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index 220445b..9043f09 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-e77d6b15f0a94b16a54ef87a84d2cabe49eb11cff5ceba76f00dd788ff483eab.yml -openapi_spec_hash: a1dab7fe72a772d188a15305124ebd73 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-80502d74c1be605e77d45ff2b54297fe34ce85dbad1e8f2dfa30ba6d09601219.yml +openapi_spec_hash: fd62f768756a400c3ecd695bfcf3845a config_hash: 1fb12ae9b478488bc1e56bfbdc210b01 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..014f0d3 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 @@ -635,6 +635,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 +647,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 +673,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 +715,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 +761,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 +770,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() @@ -797,6 +821,21 @@ private constructor( /** 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 +888,7 @@ private constructor( Options( (ignoreSelectors ?: JsonMissing.of()).map { it.toImmutable() }, model, + screenshot, selector, timeout, additionalProperties.toMutableMap(), @@ -873,6 +913,7 @@ private constructor( ignoreSelectors() model().ifPresent { it.validate() } + screenshot() selector() timeout() validated = true @@ -896,6 +937,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) @@ -1119,19 +1161,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/test/kotlin/com/browserbase/api/models/sessions/SessionExtractParamsTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractParamsTest.kt index 7ca3456..080caac 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 @@ -59,6 +59,7 @@ internal class SessionExtractParamsTest { .provider(ModelConfig.Provider.OPENAI) .build() ) + .screenshot(false) .selector("#main-content") .timeout(30000.0) .build() @@ -132,6 +133,7 @@ internal class SessionExtractParamsTest { .provider(ModelConfig.Provider.OPENAI) .build() ) + .screenshot(false) .selector("#main-content") .timeout(30000.0) .build() @@ -209,6 +211,7 @@ internal class SessionExtractParamsTest { .provider(ModelConfig.Provider.OPENAI) .build() ) + .screenshot(false) .selector("#main-content") .timeout(30000.0) .build() @@ -269,6 +272,7 @@ internal class SessionExtractParamsTest { .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/services/async/SessionServiceAsyncTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/async/SessionServiceAsyncTest.kt index 81b000b..893fefa 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 @@ -537,6 +537,7 @@ internal class SessionServiceAsyncTest { .provider(ModelConfig.Provider.OPENAI) .build() ) + .screenshot(false) .selector("#main-content") .timeout(30000.0) .build() @@ -615,6 +616,7 @@ internal class SessionServiceAsyncTest { .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/services/blocking/SessionServiceTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/blocking/SessionServiceTest.kt index 1279ff4..be3bd9f 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 @@ -534,6 +534,7 @@ internal class SessionServiceTest { .provider(ModelConfig.Provider.OPENAI) .build() ) + .screenshot(false) .selector("#main-content") .timeout(30000.0) .build() @@ -611,6 +612,7 @@ internal class SessionServiceTest { .provider(ModelConfig.Provider.OPENAI) .build() ) + .screenshot(false) .selector("#main-content") .timeout(30000.0) .build() From b37b827f38cc168d12e0d3a355f25a137796f5d7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 19:42:41 +0000 Subject: [PATCH 09/11] feat: [STG-1756] forward Vertex model config --- .stats.yml | 4 +- .../api/models/sessions/ModelConfig.kt | 3782 ++++++--- .../api/models/sessions/SessionActParams.kt | 3267 +++++++- .../models/sessions/SessionExecuteParams.kt | 7046 ++++++++++++++++- .../models/sessions/SessionExtractParams.kt | 3268 +++++++- .../models/sessions/SessionObserveParams.kt | 3268 +++++++- .../api/models/sessions/ModelConfigTest.kt | 224 +- .../models/sessions/SessionActParamsTest.kt | 234 +- .../sessions/SessionExecuteParamsTest.kt | 518 +- .../sessions/SessionExtractParamsTest.kt | 242 +- .../sessions/SessionObserveParamsTest.kt | 242 +- .../api/services/ServiceParamsTest.kt | 60 +- .../services/async/SessionServiceAsyncTest.kt | 641 +- .../services/blocking/SessionServiceTest.kt | 641 +- .../api/proguard/ProGuardCompatibilityTest.kt | 71 + 15 files changed, 21343 insertions(+), 2165 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9043f09..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-80502d74c1be605e77d45ff2b54297fe34ce85dbad1e8f2dfa30ba6d09601219.yml -openapi_spec_hash: fd62f768756a400c3ecd695bfcf3845a +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/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 da089be..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 @@ -30,497 +30,378 @@ 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 googleAuthOptions: JsonField, - private val headers: JsonField, - private val location: JsonField, - private val project: 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("googleAuthOptions") - @ExcludeMissing - googleAuthOptions: JsonField = JsonMissing.of(), - @JsonProperty("headers") @ExcludeMissing headers: JsonField = JsonMissing.of(), - @JsonProperty("location") @ExcludeMissing location: JsonField = JsonMissing.of(), - @JsonProperty("project") @ExcludeMissing project: JsonField = JsonMissing.of(), - @JsonProperty("provider") @ExcludeMissing provider: JsonField = JsonMissing.of(), - ) : this( - modelName, - apiKey, - baseUrl, - googleAuthOptions, - headers, - location, - project, - 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 - /** - * google-auth-library options used to authenticate Vertex AI models - * - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected value). - */ - fun googleAuthOptions(): Optional = - googleAuthOptions.getOptional("googleAuthOptions") + fun asVertexModelConfigObject(): VertexModelConfigObject = + vertexModelConfigObject.getOrThrow("vertexModelConfigObject") - /** - * 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 asGenericModelConfigObject(): GenericModelConfigObject = + genericModelConfigObject.getOrThrow("genericModelConfigObject") - /** - * Google Cloud location for Vertex AI models - * - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected value). - */ - fun location(): Optional = location.getOptional("location") + fun _json(): Optional = Optional.ofNullable(_json) /** - * Google Cloud project ID for Vertex AI models + * Maps this instance's current variant to a value of type [T] using the given [visitor]. * - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected value). - */ - fun project(): Optional = project.getOptional("project") - - /** - * AI provider for the model (or provide a baseURL endpoint instead) + * 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; * - * @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]. + * Optional result = modelConfig.accept(new ModelConfig.Visitor>() { + * @Override + * public Optional visitVertexModelConfigObject(VertexModelConfigObject vertexModelConfigObject) { + * return Optional.of(vertexModelConfigObject.toString()); + * } * - * 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]. + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` * - * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected type. + * @throws StagehandInvalidDataException if [Visitor.unknown] is not overridden in [visitor] and + * the current variant is unknown. */ - @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + fun accept(visitor: Visitor): T = + when { + vertexModelConfigObject != null -> + visitor.visitVertexModelConfigObject(vertexModelConfigObject) + genericModelConfigObject != null -> + visitor.visitGenericModelConfigObject(genericModelConfigObject) + else -> visitor.unknown(_json) + } - /** - * Returns the raw JSON value of [googleAuthOptions]. - * - * Unlike [googleAuthOptions], this method doesn't throw if the JSON field has an unexpected - * type. - */ - @JsonProperty("googleAuthOptions") - @ExcludeMissing - fun _googleAuthOptions(): JsonField = googleAuthOptions + private var validated: Boolean = false /** - * Returns the raw JSON value of [headers]. + * Validates that the types of all values in this object match their expected types recursively. * - * 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 [location]. + * This method is _not_ forwards compatible with new types from the API for existing fields. * - * Unlike [location], this method doesn't throw if the JSON field has an unexpected type. + * @throws StagehandInvalidDataException if any value type in this object doesn't match its + * expected type. */ - @JsonProperty("location") @ExcludeMissing fun _location(): JsonField = location + fun validate(): ModelConfig = apply { + if (validated) { + return@apply + } - /** - * 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 + 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 [provider]. + * Returns a score indicating how many valid values are contained in this object recursively. * - * Unlike [provider], this method doesn't throw if the JSON field has an unexpected type. + * Used for best match union deserialization. */ - @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonField = provider - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitVertexModelConfigObject( + vertexModelConfigObject: VertexModelConfigObject + ) = vertexModelConfigObject.validity() - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) + override fun visitGenericModelConfigObject( + genericModelConfigObject: GenericModelConfigObject + ) = genericModelConfigObject.validity() - fun toBuilder() = Builder().from(this) + override fun unknown(json: JsonValue?) = 0 + } + ) - companion object { + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - /** - * Returns a mutable builder for constructing an instance of [ModelConfig]. - * - * The following fields are required: - * ```java - * .modelName() - * ``` - */ - @JvmStatic fun builder() = Builder() + return other is ModelConfig && + vertexModelConfigObject == other.vertexModelConfigObject && + genericModelConfigObject == other.genericModelConfigObject } - /** A builder for [ModelConfig]. */ - class Builder internal constructor() { - - private var modelName: JsonField? = null - private var apiKey: JsonField = JsonMissing.of() - private var baseUrl: JsonField = JsonMissing.of() - private var googleAuthOptions: JsonField = JsonMissing.of() - private var headers: JsonField = JsonMissing.of() - private var location: JsonField = JsonMissing.of() - private var project: JsonField = JsonMissing.of() - private var provider: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() + override fun hashCode(): Int = Objects.hash(vertexModelConfigObject, genericModelConfigObject) - @JvmSynthetic - internal fun from(modelConfig: ModelConfig) = apply { - modelName = modelConfig.modelName - apiKey = modelConfig.apiKey - baseUrl = modelConfig.baseUrl - googleAuthOptions = modelConfig.googleAuthOptions - headers = modelConfig.headers - location = modelConfig.location - project = modelConfig.project - provider = modelConfig.provider - additionalProperties = modelConfig.additionalProperties.toMutableMap() + 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") } - /** 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 } + companion object { - /** API key for the model provider */ - fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + @JvmStatic + fun ofVertexModelConfigObject(vertexModelConfigObject: VertexModelConfigObject) = + ModelConfig(vertexModelConfigObject = vertexModelConfigObject) - /** - * 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 } + @JvmStatic + fun ofGenericModelConfigObject(genericModelConfigObject: GenericModelConfigObject) = + ModelConfig(genericModelConfigObject = genericModelConfigObject) + } - /** Base URL for the model provider */ - fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) + /** + * An interface that defines how to map each variant of [ModelConfig] to a value of type [T]. + */ + interface Visitor { - /** - * 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 } + fun visitVertexModelConfigObject(vertexModelConfigObject: VertexModelConfigObject): T - /** google-auth-library options used to authenticate Vertex AI models */ - fun googleAuthOptions(googleAuthOptions: GoogleAuthOptions) = - googleAuthOptions(JsonField.of(googleAuthOptions)) + fun visitGenericModelConfigObject(genericModelConfigObject: GenericModelConfigObject): T /** - * Sets [Builder.googleAuthOptions] to an arbitrary JSON value. + * 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. * - * You should usually call [Builder.googleAuthOptions] with a well-typed [GoogleAuthOptions] - * value instead. This method is primarily for setting the field to an undocumented or not - * yet supported value. + * @throws StagehandInvalidDataException in the default implementation. */ - fun googleAuthOptions(googleAuthOptions: JsonField) = apply { - this.googleAuthOptions = googleAuthOptions + fun unknown(json: JsonValue?): T { + throw StagehandInvalidDataException("Unknown ModelConfig: $json") } + } - /** Custom headers sent with every request to the model provider */ - fun headers(headers: Headers) = headers(JsonField.of(headers)) + internal class Deserializer : BaseDeserializer(ModelConfig::class) { - /** - * 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 } + 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() + } + } + } - /** Google Cloud location for Vertex AI models */ - fun location(location: String) = location(JsonField.of(location)) + internal class Serializer : BaseSerializer(ModelConfig::class) { - /** - * 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 } + 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, + ) { - /** Google Cloud project ID for Vertex AI models */ - fun project(project: String) = project(JsonField.of(project)) + @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(), + ) /** - * Sets [Builder.project] to an arbitrary JSON value. + * Vertex provider authentication configuration * - * 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. + * @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(project: JsonField) = apply { this.project = project } - - /** AI provider for the model (or provide a baseURL endpoint instead) */ - fun provider(provider: Provider) = provider(JsonField.of(provider)) + fun auth(): Auth = auth.getRequired("auth") /** - * Sets [Builder.provider] to an arbitrary JSON value. + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano') * - * 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 or is + * unexpectedly missing or null (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 modelName(): String = modelName.getRequired("modelName") /** - * Returns an immutable instance of [ModelConfig]. + * Vertex AI model provider * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: + * Expected to always return the following: * ```java - * .modelName() + * JsonValue.from("vertex") * ``` * - * @throws IllegalStateException if any required field is unset. + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). */ - fun build(): ModelConfig = - ModelConfig( - checkRequired("modelName", modelName), - apiKey, - baseUrl, - googleAuthOptions, - headers, - location, - project, - 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(): ModelConfig = apply { - if (validated) { - return@apply - } - - modelName() - apiKey() - baseUrl() - googleAuthOptions().ifPresent { it.validate() } - headers().ifPresent { it.validate() } - location() - project() - 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) + - (googleAuthOptions.asKnown().getOrNull()?.validity() ?: 0) + - (headers.asKnown().getOrNull()?.validity() ?: 0) + - (if (location.asKnown().isPresent) 1 else 0) + - (if (project.asKnown().isPresent) 1 else 0) + - (provider.asKnown().getOrNull()?.validity() ?: 0) - - /** google-auth-library options used to authenticate Vertex AI models */ - class GoogleAuthOptions - @JsonCreator(mode = JsonCreator.Mode.DISABLED) - private constructor( - private val credentials: JsonField, - private val projectId: JsonField, - private val scopes: JsonField, - private val universeDomain: JsonField, - private val additionalProperties: MutableMap, - ) { + @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonValue = provider - @JsonCreator - private constructor( - @JsonProperty("credentials") - @ExcludeMissing - credentials: JsonField = 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, projectId, scopes, universeDomain, mutableMapOf()) + /** + * 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") /** - * Google Cloud service account credentials + * 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 credentials(): Optional = credentials.getOptional("credentials") + fun apiKey(): Optional = apiKey.getOptional("apiKey") /** - * Google Cloud project ID used by google-auth-library + * 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 projectId(): Optional = projectId.getOptional("projectId") + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") /** - * Google auth scopes for the desired API request + * 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 scopes(): Optional = scopes.getOptional("scopes") + fun headers(): Optional = headers.getOptional("headers") /** - * Google Cloud universe domain + * Returns the raw JSON value of [auth]. * - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). + * 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. */ - fun universeDomain(): Optional = universeDomain.getOptional("universeDomain") + @JsonProperty("modelName") @ExcludeMissing fun _modelName(): JsonField = modelName /** - * Returns the raw JSON value of [credentials]. + * Returns the raw JSON value of [providerOptions]. * - * Unlike [credentials], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [providerOptions], this method doesn't throw if the JSON field has an unexpected + * type. */ - @JsonProperty("credentials") + @JsonProperty("providerOptions") @ExcludeMissing - fun _credentials(): JsonField = credentials + fun _providerOptions(): JsonField = providerOptions /** - * Returns the raw JSON value of [projectId]. + * Returns the raw JSON value of [apiKey]. * - * Unlike [projectId], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("projectId") @ExcludeMissing fun _projectId(): JsonField = projectId + @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey /** - * Returns the raw JSON value of [scopes]. + * Returns the raw JSON value of [baseUrl]. * - * Unlike [scopes], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("scopes") @ExcludeMissing fun _scopes(): JsonField = scopes + @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl /** - * Returns the raw JSON value of [universeDomain]. + * Returns the raw JSON value of [headers]. * - * Unlike [universeDomain], this method doesn't throw if the JSON field has an unexpected - * type. + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("universeDomain") - @ExcludeMissing - fun _universeDomain(): JsonField = universeDomain + @JsonProperty("headers") @ExcludeMissing fun _headers(): JsonField = headers @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -536,86 +417,131 @@ private constructor( companion object { - /** Returns a mutable builder for constructing an instance of [GoogleAuthOptions]. */ + /** + * 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 [GoogleAuthOptions]. */ + /** A builder for [VertexModelConfigObject]. */ class Builder internal constructor() { - private var credentials: JsonField = JsonMissing.of() - private var projectId: JsonField = JsonMissing.of() - private var scopes: JsonField = JsonMissing.of() - private var universeDomain: JsonField = JsonMissing.of() + 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(googleAuthOptions: GoogleAuthOptions) = apply { - credentials = googleAuthOptions.credentials - projectId = googleAuthOptions.projectId - scopes = googleAuthOptions.scopes - universeDomain = googleAuthOptions.universeDomain - additionalProperties = googleAuthOptions.additionalProperties.toMutableMap() + 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() } - /** Google Cloud service account credentials */ - fun credentials(credentials: Credentials) = credentials(JsonField.of(credentials)) + /** Vertex provider authentication configuration */ + fun auth(auth: Auth) = auth(JsonField.of(auth)) /** - * Sets [Builder.credentials] to an arbitrary JSON value. + * Sets [Builder.auth] 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. + * 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 credentials(credentials: JsonField) = apply { - this.credentials = credentials - } + fun auth(auth: JsonField) = apply { this.auth = auth } - /** Google Cloud project ID used by google-auth-library */ - fun projectId(projectId: String) = projectId(JsonField.of(projectId)) + /** Model name string with provider prefix (e.g., 'openai/gpt-5-nano') */ + fun modelName(modelName: String) = modelName(JsonField.of(modelName)) /** - * Sets [Builder.projectId] to an arbitrary JSON value. + * Sets [Builder.modelName] to an arbitrary JSON value. * - * You should usually call [Builder.projectId] with a well-typed [String] value instead. + * 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 projectId(projectId: JsonField) = apply { this.projectId = projectId } + fun modelName(modelName: JsonField) = apply { this.modelName = modelName } - /** Google auth scopes for the desired API request */ - fun scopes(scopes: Scopes) = scopes(JsonField.of(scopes)) + /** + * 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.scopes] to an arbitrary JSON value. + * Sets [Builder.providerOptions] to an arbitrary JSON value. * - * You should usually call [Builder.scopes] with a well-typed [Scopes] value instead. + * 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 scopes(scopes: JsonField) = apply { this.scopes = scopes } + fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } - /** Alias for calling [scopes] with `Scopes.ofString(string)`. */ - fun scopes(string: String) = scopes(Scopes.ofString(string)) + /** Base URL for the model provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) - /** Alias for calling [scopes] with `Scopes.ofStrings(strings)`. */ - fun scopesOfStrings(strings: List) = scopes(Scopes.ofStrings(strings)) + /** + * 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 } - /** Google Cloud universe domain */ - fun universeDomain(universeDomain: String) = - universeDomain(JsonField.of(universeDomain)) + /** Custom headers sent with every request to the model provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) /** - * Sets [Builder.universeDomain] to an arbitrary JSON value. + * Sets [Builder.headers] 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 + * 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 universeDomain(universeDomain: JsonField) = apply { - this.universeDomain = universeDomain - } + fun headers(headers: JsonField) = apply { this.headers = headers } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -637,16 +563,28 @@ private constructor( } /** - * Returns an immutable instance of [GoogleAuthOptions]. + * 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(): GoogleAuthOptions = - GoogleAuthOptions( - credentials, - projectId, - scopes, - universeDomain, + fun build(): VertexModelConfigObject = + VertexModelConfigObject( + checkRequired("auth", auth), + checkRequired("modelName", modelName), + provider, + checkRequired("providerOptions", providerOptions), + apiKey, + baseUrl, + headers, additionalProperties.toMutableMap(), ) } @@ -662,15 +600,22 @@ private constructor( * @throws StagehandInvalidDataException if any value type in this object doesn't match its * expected type. */ - fun validate(): GoogleAuthOptions = apply { + fun validate(): VertexModelConfigObject = apply { if (validated) { return@apply } - credentials().ifPresent { it.validate() } - projectId() - scopes().ifPresent { it.validate() } - universeDomain() + 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 } @@ -690,211 +635,98 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (credentials.asKnown().getOrNull()?.validity() ?: 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 + (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 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 credentials: JsonField, + private val type: JsonValue, private val projectId: JsonField, - private val tokenUri: JsonField, - private val type: JsonField, + private val scopes: 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") + @JsonProperty("credentials") @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") + credentials: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("projectId") @ExcludeMissing projectId: JsonField = JsonMissing.of(), - @JsonProperty("token_uri") + @JsonProperty("scopes") @ExcludeMissing - tokenUri: JsonField = JsonMissing.of(), - @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), - @JsonProperty("universe_domain") + scopes: JsonField = JsonMissing.of(), + @JsonProperty("universeDomain") @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") + ) : 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 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") + fun credentials(): Credentials = credentials.getRequired("credentials") /** - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. - * if the server responded with an unexpected value). + * 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). */ - fun projectId(): Optional = projectId.getOptional("project_id") + @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 tokenUri(): Optional = tokenUri.getOptional("token_uri") + 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 type(): Optional = type.getOptional("type") + 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("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 + fun universeDomain(): Optional = universeDomain.getOptional("universeDomain") /** - * Returns the raw JSON value of [privateKeyId]. + * Returns the raw JSON value of [credentials]. * - * Unlike [privateKeyId], this method doesn't throw if the JSON field has an unexpected + * Unlike [credentials], this method doesn't throw if the JSON field has an unexpected * type. */ - @JsonProperty("private_key_id") + @JsonProperty("credentials") @ExcludeMissing - fun _privateKeyId(): JsonField = privateKeyId + fun _credentials(): JsonField = credentials /** * Returns the raw JSON value of [projectId]. @@ -902,24 +734,16 @@ private constructor( * Unlike [projectId], this method doesn't throw if the JSON field has an unexpected * type. */ - @JsonProperty("project_id") + @JsonProperty("projectId") @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]. + * Returns the raw JSON value of [scopes]. * - * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [scopes], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + @JsonProperty("scopes") @ExcludeMissing fun _scopes(): JsonField = scopes /** * Returns the raw JSON value of [universeDomain]. @@ -927,7 +751,7 @@ private constructor( * Unlike [universeDomain], this method doesn't throw if the JSON field has an * unexpected type. */ - @JsonProperty("universe_domain") + @JsonProperty("universeDomain") @ExcludeMissing fun _universeDomain(): JsonField = universeDomain @@ -946,138 +770,65 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [Credentials]. + * Returns a mutable builder for constructing an instance of [Auth]. * * The following fields are required: * ```java - * .clientEmail() - * .privateKey() + * .credentials() * ``` */ @JvmStatic fun builder() = Builder() } - /** A builder for [Credentials]. */ + /** A builder for [Auth]. */ 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 credentials: JsonField? = null + private var type: JsonValue = JsonValue.from("googleServiceAccount") private var projectId: JsonField = JsonMissing.of() - private var tokenUri: JsonField = JsonMissing.of() - private var type: JsonField = JsonMissing.of() + private var scopes: 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 + 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 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)) + /** Google Cloud service account credentials */ + fun credentials(credentials: Credentials) = credentials(JsonField.of(credentials)) /** - * Sets [Builder.clientX509CertUrl] to an arbitrary JSON value. + * Sets [Builder.credentials] to an arbitrary JSON value. * - * You should usually call [Builder.clientX509CertUrl] with a well-typed [String] + * 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 clientX509CertUrl(clientX509CertUrl: JsonField) = apply { - this.clientX509CertUrl = clientX509CertUrl + fun credentials(credentials: JsonField) = apply { + this.credentials = credentials } - fun privateKeyId(privateKeyId: String) = privateKeyId(JsonField.of(privateKeyId)) - /** - * Sets [Builder.privateKeyId] to an arbitrary JSON value. + * Sets the field 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. + * 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 privateKeyId(privateKeyId: JsonField) = apply { - this.privateKeyId = privateKeyId - } + 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)) /** @@ -1089,28 +840,25 @@ private constructor( */ fun projectId(projectId: JsonField) = apply { this.projectId = projectId } - fun tokenUri(tokenUri: String) = tokenUri(JsonField.of(tokenUri)) + /** Google auth scopes for the desired API request */ + fun scopes(scopes: Scopes) = scopes(JsonField.of(scopes)) /** - * Sets [Builder.tokenUri] to an arbitrary JSON value. + * Sets [Builder.scopes] to an arbitrary JSON value. * - * You should usually call [Builder.tokenUri] with a well-typed [String] 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 tokenUri(tokenUri: JsonField) = apply { this.tokenUri = tokenUri } + fun scopes(scopes: JsonField) = apply { this.scopes = scopes } - fun type(type: Type) = type(JsonField.of(type)) + /** Alias for calling [scopes] with `Scopes.ofString(string)`. */ + fun scopes(string: String) = scopes(Scopes.ofString(string)) - /** - * 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 } + /** 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)) @@ -1148,30 +896,23 @@ private constructor( } /** - * Returns an immutable instance of [Credentials]. + * Returns an immutable instance of [Auth]. * * Further updates to this [Builder] will not mutate the returned instance. * * The following fields are required: * ```java - * .clientEmail() - * .privateKey() + * .credentials() * ``` * * @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, + fun build(): Auth = + Auth( + checkRequired("credentials", credentials), type, + projectId, + scopes, universeDomain, additionalProperties.toMutableMap(), ) @@ -1189,21 +930,19 @@ private constructor( * @throws StagehandInvalidDataException if any value type in this object doesn't match * its expected type. */ - fun validate(): Credentials = apply { + fun validate(): Auth = apply { if (validated) { return@apply } - clientEmail() - privateKey() - authProviderX509CertUrl() - authUri() - clientId() - clientX509CertUrl() - privateKeyId() + credentials().validate() + _type().let { + if (it != JsonValue.from("googleServiceAccount")) { + throw StagehandInvalidDataException("'type' is invalid, received $it") + } + } projectId() - tokenUri() - type().ifPresent { it.validate() } + scopes().ifPresent { it.validate() } universeDomain() validated = true } @@ -1224,150 +963,1564 @@ private constructor( */ @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) + + (credentials.asKnown().getOrNull()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("googleServiceAccount")) 1 else 0 } + (if (projectId.asKnown().isPresent) 1 else 0) + - (if (tokenUri.asKnown().isPresent) 1 else 0) + - (type.asKnown().getOrNull()?.validity() ?: 0) + + (scopes.asKnown().getOrNull()?.validity() ?: 0) + (if (universeDomain.asKnown().isPresent) 1 else 0) - class Type @JsonCreator private constructor(private val value: JsonField) : - Enum { + /** 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(), + ) /** - * 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. + * @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). */ - @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + fun clientEmail(): String = clientEmail.getRequired("client_email") - companion object { + /** + * @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") - @JvmField val SERVICE_ACCOUNT = of("service_account") + /** + * @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") - @JvmStatic fun of(value: String) = Type(JsonField.of(value)) - } + /** + * @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") - /** An enum containing [Type]'s known values. */ - enum class Known { - SERVICE_ACCOUNT - } + /** + * @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") /** - * 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. + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). */ - enum class Value { - SERVICE_ACCOUNT, - /** - * An enum member indicating that [Type] was instantiated with an unknown value. - */ - _UNKNOWN, - } + fun clientX509CertUrl(): Optional = + clientX509CertUrl.getOptional("client_x509_cert_url") /** - * 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. + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). */ - fun value(): Value = - when (this) { - SERVICE_ACCOUNT -> Value.SERVICE_ACCOUNT - else -> Value._UNKNOWN - } + fun privateKeyId(): Optional = privateKeyId.getOptional("private_key_id") /** - * 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. + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). */ - fun known(): Known = - when (this) { - SERVICE_ACCOUNT -> Known.SERVICE_ACCOUNT - else -> throw StagehandInvalidDataException("Unknown Type: $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 this class instance's primitive wire representation. + * Returns the raw JSON value of [clientEmail]. * - * This differs from the [toString] method because that method is primarily for - * debugging and generally doesn't throw. + * 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]. * - * @throws StagehandInvalidDataException if this class instance's value does not - * have the expected primitive type. + * Unlike [privateKey], this method doesn't throw if the JSON field has an + * unexpected type. */ - fun asString(): String = - _value().asString().orElseThrow { - StagehandInvalidDataException("Value is not a String") - } + @JsonProperty("private_key") + @ExcludeMissing + fun _privateKey(): JsonField = privateKey - private var validated: Boolean = false + /** + * 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 /** - * Validates that the types of all values in this object match their expected types - * recursively. + * Returns the raw JSON value of [authUri]. * - * This method is _not_ forwards compatible with new types from the API for existing - * fields. + * 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]. * - * @throws StagehandInvalidDataException if any value type in this object doesn't - * match its expected type. + * Unlike [clientId], this method doesn't throw if the JSON field has an unexpected + * type. */ - fun validate(): Type = apply { - if (validated) { - return@apply - } + @JsonProperty("client_id") + @ExcludeMissing + fun _clientId(): JsonField = clientId - known() - validated = true - } + /** + * 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 - fun isValid(): Boolean = - try { - validate() - true - } catch (e: StagehandInvalidDataException) { - false - } + /** + * 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 a score indicating how many valid values are contained in this object - * recursively. + * Returns the raw JSON value of [projectId]. * - * Used for best match union deserialization. + * Unlike [projectId], this method doesn't throw if the JSON field has an unexpected + * type. */ - @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + @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 Type && value == other.value + 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() = value.hashCode() + override fun hashCode(): Int = hashCode - override fun toString() = value.toString() + override fun toString() = + "Vertex{location=$location, project=$project, baseUrl=$baseUrl, headers=$headers, additionalProperties=$additionalProperties}" } override fun equals(other: Any?): Boolean { @@ -1375,105 +2528,79 @@ private constructor( 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 && + return other is ProviderOptions && + vertex == other.vertex && additionalProperties == other.additionalProperties } - private val hashCode: Int by lazy { - Objects.hash( - clientEmail, - privateKey, - authProviderX509CertUrl, - authUri, - clientId, - clientX509CertUrl, - privateKeyId, - projectId, - tokenUri, - type, - universeDomain, - additionalProperties, - ) - } + private val hashCode: Int by lazy { Objects.hash(vertex, 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}" + "ProviderOptions{vertex=$vertex, additionalProperties=$additionalProperties}" } - /** Google auth scopes for the desired API request */ - @JsonDeserialize(using = Scopes.Deserializer::class) - @JsonSerialize(using = Scopes.Serializer::class) - class Scopes + /** Custom headers sent with every request to the model provider */ + class Headers + @JsonCreator private constructor( - private val string: String? = null, - private val strings: List? = null, - private val _json: JsonValue? = null, + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map ) { - fun string(): Optional = Optional.ofNullable(string) + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties - fun strings(): Optional> = Optional.ofNullable(strings) + fun toBuilder() = Builder().from(this) - fun isString(): Boolean = string != null + companion object { - fun isStrings(): Boolean = strings != null + /** Returns a mutable builder for constructing an instance of [Headers]. */ + @JvmStatic fun builder() = Builder() + } - fun asString(): String = string.getOrThrow("string") + /** A builder for [Headers]. */ + class Builder internal constructor() { - fun asStrings(): List = strings.getOrThrow("strings") + private var additionalProperties: MutableMap = mutableMapOf() - fun _json(): Optional = Optional.ofNullable(_json) + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.additionalProperties.toMutableMap() + } - /** - * 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) + 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 /** @@ -1486,18 +2613,11 @@ private constructor( * @throws StagehandInvalidDataException if any value type in this object doesn't match * its expected type. */ - fun validate(): Scopes = apply { + fun validate(): Headers = apply { if (validated) { return@apply } - accept( - object : Visitor { - override fun visitString(string: String) {} - - override fun visitStrings(strings: List) {} - } - ) validated = true } @@ -1517,111 +2637,21 @@ private constructor( */ @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 - } - ) + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } 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") - } + return other is Headers && additionalProperties == other.additionalProperties } - 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() - } - } - } + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } - internal class Serializer : BaseSerializer(Scopes::class) { + override fun hashCode(): Int = hashCode - 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 toString() = "Headers{additionalProperties=$additionalProperties}" } override fun equals(other: Any?): Boolean { @@ -1629,53 +2659,239 @@ private constructor( return true } - return other is GoogleAuthOptions && - credentials == other.credentials && - projectId == other.projectId && - scopes == other.scopes && - universeDomain == other.universeDomain && + 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(credentials, projectId, scopes, universeDomain, additionalProperties) + Objects.hash( + auth, + modelName, + provider, + providerOptions, + apiKey, + baseUrl, + headers, + additionalProperties, + ) } override fun hashCode(): Int = hashCode override fun toString() = - "GoogleAuthOptions{credentials=$credentials, projectId=$projectId, scopes=$scopes, universeDomain=$universeDomain, additionalProperties=$additionalProperties}" + "VertexModelConfigObject{auth=$auth, modelName=$modelName, provider=$provider, providerOptions=$providerOptions, apiKey=$apiKey, baseUrl=$baseUrl, headers=$headers, additionalProperties=$additionalProperties}" } - /** Custom headers sent with every request to the model provider */ - class Headers - @JsonCreator + class GenericModelConfigObject + @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - @com.fasterxml.jackson.annotation.JsonValue - private val additionalProperties: Map + 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 = additionalProperties + 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 } - fun toBuilder() = Builder().from(this) + /** Base URL for the model provider */ + fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) - companion object { + /** + * 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 } - /** Returns a mutable builder for constructing an instance of [Headers]. */ - @JvmStatic fun builder() = Builder() - } + /** Custom headers sent with every request to the model provider */ + fun headers(headers: Headers) = headers(JsonField.of(headers)) - /** A builder for [Headers]. */ - class Builder internal constructor() { + /** + * 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 } - private var additionalProperties: MutableMap = mutableMapOf() + /** AI provider for the model (or provide a baseURL endpoint instead) */ + fun provider(provider: Provider) = provider(JsonField.of(provider)) - @JvmSynthetic - internal fun from(headers: Headers) = apply { - additionalProperties = headers.additionalProperties.toMutableMap() - } + /** + * 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() @@ -1697,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 @@ -1715,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 } @@ -1739,217 +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) + } + + 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()) + } - @JvmField val OPENAI = of("openai") + 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 + } - @JvmField val ANTHROPIC = of("anthropic") + validated = true + } - @JvmField val GOOGLE = of("google") + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + false + } - @JvmField val MICROSOFT = of("microsoft") + /** + * Returns 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() } - @JvmField val BEDROCK = of("bedrock") + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - @JvmField val VERTEX = of("vertex") + return other is Headers && additionalProperties == other.additionalProperties + } - @JvmStatic fun of(value: String) = Provider(JsonField.of(value)) - } + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } - /** An enum containing [Provider]'s known values. */ - enum class Known { - OPENAI, - ANTHROPIC, - GOOGLE, - MICROSOFT, - BEDROCK, - VERTEX, - } + override fun hashCode(): Int = hashCode - /** - * 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, - VERTEX, - /** An enum member indicating that [Provider] was instantiated with an unknown value. */ - _UNKNOWN, + 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 - VERTEX -> Value.VERTEX - 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 - VERTEX -> Known.VERTEX - 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 && - googleAuthOptions == other.googleAuthOptions && - headers == other.headers && - location == other.location && - project == other.project && - provider == other.provider && - additionalProperties == other.additionalProperties - } + override fun hashCode(): Int = hashCode - private val hashCode: Int by lazy { - Objects.hash( - modelName, - apiKey, - baseUrl, - googleAuthOptions, - headers, - location, - project, - 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, googleAuthOptions=$googleAuthOptions, headers=$headers, location=$location, project=$project, 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 014f0d3..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 @@ -815,8 +816,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)) @@ -946,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") @@ -978,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()); * } * * // ... @@ -997,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) } @@ -1021,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) {} @@ -1049,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 @@ -1062,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") @@ -1077,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) } @@ -1087,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 @@ -1113,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) }, @@ -1144,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 { 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 dd8ffaa..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 @@ -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 @@ -757,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)) @@ -889,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") @@ -921,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()); * } * * // ... @@ -940,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) } @@ -964,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) {} @@ -992,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 @@ -1005,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") @@ -1020,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) } @@ -1030,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 @@ -1056,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) }, @@ -1087,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}" + } } /** 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 6005d44..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,23 +4,24 @@ 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() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + fun ofVertexModelConfigObject() { + val vertexModelConfigObject = + ModelConfig.VertexModelConfigObject.builder() + .auth( + ModelConfig.VertexModelConfigObject.Auth.builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + ModelConfig.VertexModelConfigObject.Auth.Credentials.builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -31,7 +32,8 @@ internal class ModelConfigTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type.SERVICE_ACCOUNT + ModelConfig.VertexModelConfigObject.Auth.Credentials.Type + .SERVICE_ACCOUNT ) .universeDomain("universe_domain") .build() @@ -41,95 +43,148 @@ internal class ModelConfigTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .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.googleAuthOptions()) - .contains( - ModelConfig.GoogleAuthOptions.builder() - .credentials( - ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type.SERVICE_ACCOUNT) - .universeDomain("universe_domain") + val modelConfig = ModelConfig.ofVertexModelConfigObject(vertexModelConfigObject) + + assertThat(modelConfig.vertexModelConfigObject()).contains(vertexModelConfigObject) + assertThat(modelConfig.genericModelConfigObject()).isEmpty + } + + @Test + fun ofVertexModelConfigObjectRoundtrip() { + 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() ) - .projectId("projectId") - .scopes("string") - .universeDomain("universeDomain") .build() ) - assertThat(modelConfig.headers()) - .contains( - ModelConfig.Headers.builder() - .putAdditionalProperty("foo", JsonValue.from("string")) - .build() + + val roundtrippedModelConfig = + jsonMapper.readValue( + jsonMapper.writeValueAsString(modelConfig), + jacksonTypeRef(), ) - assertThat(modelConfig.location()).contains("us-central1") - assertThat(modelConfig.project()).contains("my-gcp-project") - assertThat(modelConfig.provider()).contains(ModelConfig.Provider.OPENAI) + + assertThat(roundtrippedModelConfig).isEqualTo(modelConfig) } @Test - fun roundtrip() { - val jsonMapper = jsonMapper() - val modelConfig = - ModelConfig.builder() + 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") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() - .credentials( - ModelConfig.GoogleAuthOptions.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.GoogleAuthOptions.Credentials.Type.SERVICE_ACCOUNT - ) - .universeDomain("universe_domain") - .build() - ) - .projectId("projectId") - .scopes("string") - .universeDomain("universeDomain") - .build() - ) .headers( - ModelConfig.Headers.builder() + ModelConfig.GenericModelConfigObject.Headers.builder() .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) - .location("us-central1") - .project("my-gcp-project") - .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), @@ -138,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 2d38cf9..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,14 +19,14 @@ internal class SessionActParamsTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -37,7 +37,11 @@ internal class SessionActParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -48,14 +52,44 @@ internal class SessionActParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) @@ -102,14 +136,15 @@ internal class SessionActParamsTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionActParams.Options.Model.VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -120,7 +155,11 @@ internal class SessionActParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -131,14 +170,44 @@ internal class SessionActParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) @@ -189,14 +258,15 @@ internal class SessionActParamsTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionActParams.Options.Model.VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -207,7 +277,11 @@ internal class SessionActParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -218,14 +292,44 @@ internal class SessionActParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) @@ -256,14 +360,14 @@ internal class SessionActParamsTest { .contains( SessionActParams.Options.builder() .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -274,7 +378,11 @@ internal class SessionActParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -285,14 +393,44 @@ internal class SessionActParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .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 351b711..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,14 +18,19 @@ internal class SessionExecuteParamsTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.ExecutionModel.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -36,7 +41,11 @@ internal class SessionExecuteParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -47,26 +56,62 @@ internal class SessionExecuteParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject.builder() + .auth( + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -77,7 +122,11 @@ internal class SessionExecuteParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -88,14 +137,46 @@ internal class SessionExecuteParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) @@ -153,14 +234,19 @@ internal class SessionExecuteParamsTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.ExecutionModel.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -171,7 +257,11 @@ internal class SessionExecuteParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -182,26 +272,63 @@ internal class SessionExecuteParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject.builder() + .auth( + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -212,7 +339,11 @@ internal class SessionExecuteParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -223,14 +354,46 @@ internal class SessionExecuteParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) @@ -292,14 +455,19 @@ internal class SessionExecuteParamsTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.ExecutionModel.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -310,7 +478,11 @@ internal class SessionExecuteParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -321,26 +493,63 @@ internal class SessionExecuteParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject.builder() + .auth( + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -351,7 +560,11 @@ internal class SessionExecuteParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -362,14 +575,46 @@ internal class SessionExecuteParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) @@ -403,14 +648,19 @@ internal class SessionExecuteParamsTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.ExecutionModel.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -421,7 +671,11 @@ internal class SessionExecuteParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -432,26 +686,62 @@ internal class SessionExecuteParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject.builder() + .auth( + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -462,7 +752,11 @@ internal class SessionExecuteParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -473,14 +767,46 @@ internal class SessionExecuteParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .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 080caac..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,14 +20,15 @@ internal class SessionExtractParamsTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -38,7 +39,11 @@ internal class SessionExtractParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -49,14 +54,44 @@ internal class SessionExtractParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .screenshot(false) @@ -94,14 +129,16 @@ internal class SessionExtractParamsTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -112,7 +149,11 @@ internal class SessionExtractParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -123,14 +164,46 @@ internal class SessionExtractParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .screenshot(false) @@ -172,14 +245,16 @@ internal class SessionExtractParamsTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -190,7 +265,11 @@ internal class SessionExtractParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -201,14 +280,46 @@ internal class SessionExtractParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .screenshot(false) @@ -233,14 +344,15 @@ internal class SessionExtractParamsTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -251,7 +363,11 @@ internal class SessionExtractParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -262,14 +378,44 @@ internal class SessionExtractParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .screenshot(false) 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 3c7b1c1..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 @@ -20,14 +20,15 @@ internal class SessionObserveParamsTest { SessionObserveParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -38,7 +39,11 @@ internal class SessionObserveParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -49,14 +54,44 @@ internal class SessionObserveParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .selector("nav") @@ -102,14 +137,16 @@ internal class SessionObserveParamsTest { SessionObserveParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -120,7 +157,11 @@ internal class SessionObserveParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -131,14 +172,46 @@ internal class SessionObserveParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .selector("nav") @@ -188,14 +261,16 @@ internal class SessionObserveParamsTest { SessionObserveParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -206,7 +281,11 @@ internal class SessionObserveParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -217,14 +296,46 @@ internal class SessionObserveParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .selector("nav") @@ -256,14 +367,15 @@ internal class SessionObserveParamsTest { SessionObserveParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -274,7 +386,11 @@ internal class SessionObserveParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -285,14 +401,44 @@ internal class SessionObserveParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .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 f36435f..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,14 +233,15 @@ internal class ServiceParamsTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionActParams.Options.Model.VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -252,7 +252,11 @@ internal class ServiceParamsTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials.Type + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .Type .SERVICE_ACCOUNT ) .universeDomain("universe_domain") @@ -263,14 +267,44 @@ internal class ServiceParamsTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .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 893fefa..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,14 +38,16 @@ internal class SessionServiceAsyncTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -57,7 +58,10 @@ internal class SessionServiceAsyncTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -69,14 +73,46 @@ internal class SessionServiceAsyncTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) @@ -124,14 +160,16 @@ internal class SessionServiceAsyncTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -142,7 +180,10 @@ internal class SessionServiceAsyncTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -154,14 +195,46 @@ internal class SessionServiceAsyncTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) @@ -232,14 +305,20 @@ internal class SessionServiceAsyncTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -250,7 +329,11 @@ internal class SessionServiceAsyncTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionExecuteParams.AgentConfig + .ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -262,26 +345,66 @@ internal class SessionServiceAsyncTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -292,7 +415,10 @@ internal class SessionServiceAsyncTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -304,14 +430,48 @@ internal class SessionServiceAsyncTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) @@ -363,14 +523,20 @@ internal class SessionServiceAsyncTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -381,7 +547,11 @@ internal class SessionServiceAsyncTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionExecuteParams.AgentConfig + .ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -393,26 +563,66 @@ internal class SessionServiceAsyncTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -423,7 +633,10 @@ internal class SessionServiceAsyncTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -435,14 +648,48 @@ internal class SessionServiceAsyncTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) @@ -497,14 +744,17 @@ internal class SessionServiceAsyncTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -515,7 +765,10 @@ internal class SessionServiceAsyncTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -527,14 +780,46 @@ internal class SessionServiceAsyncTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .screenshot(false) @@ -576,14 +861,17 @@ internal class SessionServiceAsyncTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -594,7 +882,10 @@ internal class SessionServiceAsyncTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -606,14 +897,46 @@ internal class SessionServiceAsyncTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .screenshot(false) @@ -689,14 +1012,17 @@ internal class SessionServiceAsyncTest { SessionObserveParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -707,7 +1033,10 @@ internal class SessionServiceAsyncTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -719,14 +1048,46 @@ internal class SessionServiceAsyncTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .selector("nav") @@ -776,14 +1137,17 @@ internal class SessionServiceAsyncTest { SessionObserveParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -794,7 +1158,10 @@ internal class SessionServiceAsyncTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -806,14 +1173,46 @@ internal class SessionServiceAsyncTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .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 be3bd9f..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,14 +38,16 @@ internal class SessionServiceTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -57,7 +58,10 @@ internal class SessionServiceTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -69,14 +73,46 @@ internal class SessionServiceTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) @@ -123,14 +159,16 @@ internal class SessionServiceTest { .options( SessionActParams.Options.builder() .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionActParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionActParams.Options.Model.VertexModelConfigObject.Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -141,7 +179,10 @@ internal class SessionServiceTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionActParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -153,14 +194,46 @@ internal class SessionServiceTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .timeout(30000.0) @@ -230,14 +303,20 @@ internal class SessionServiceTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -248,7 +327,11 @@ internal class SessionServiceTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionExecuteParams.AgentConfig + .ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -260,26 +343,66 @@ internal class SessionServiceTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -290,7 +413,10 @@ internal class SessionServiceTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -302,14 +428,48 @@ internal class SessionServiceTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) @@ -360,14 +520,20 @@ internal class SessionServiceTest { SessionExecuteParams.AgentConfig.builder() .cua(true) .executionModel( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -378,7 +544,11 @@ internal class SessionServiceTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionExecuteParams.AgentConfig + .ExecutionModel + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -390,26 +560,66 @@ internal class SessionServiceTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .mode(SessionExecuteParams.AgentConfig.Mode.CUA) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExecuteParams.AgentConfig.Model.VertexModelConfigObject + .builder() + .auth( + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -420,7 +630,10 @@ internal class SessionServiceTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionExecuteParams.AgentConfig.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -432,14 +645,48 @@ internal class SessionServiceTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) @@ -494,14 +741,17 @@ internal class SessionServiceTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -512,7 +762,10 @@ internal class SessionServiceTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -524,14 +777,46 @@ internal class SessionServiceTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .screenshot(false) @@ -572,14 +857,17 @@ internal class SessionServiceTest { SessionExtractParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionExtractParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionExtractParams.Options.Model.VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -590,7 +878,10 @@ internal class SessionServiceTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionExtractParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -602,14 +893,46 @@ internal class SessionServiceTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .screenshot(false) @@ -684,14 +1007,17 @@ internal class SessionServiceTest { SessionObserveParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -702,7 +1028,10 @@ internal class SessionServiceTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -714,14 +1043,46 @@ internal class SessionServiceTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .selector("nav") @@ -770,14 +1131,17 @@ internal class SessionServiceTest { SessionObserveParams.Options.builder() .ignoreSelectors(listOf("nav", ".cookie-banner", "#sidebar-ads")) .model( - ModelConfig.builder() - .modelName("openai/gpt-5.4-mini") - .apiKey("sk-some-openai-api-key") - .baseUrl("https://api.openai.com/v1") - .googleAuthOptions( - ModelConfig.GoogleAuthOptions.builder() + SessionObserveParams.Options.Model.VertexModelConfigObject.builder() + .auth( + SessionObserveParams.Options.Model.VertexModelConfigObject + .Auth + .builder() .credentials( - ModelConfig.GoogleAuthOptions.Credentials.builder() + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials + .builder() .clientEmail("client_email") .privateKey("private_key") .authProviderX509CertUrl("https://example.com") @@ -788,7 +1152,10 @@ internal class SessionServiceTest { .projectId("project_id") .tokenUri("https://example.com") .type( - ModelConfig.GoogleAuthOptions.Credentials + SessionObserveParams.Options.Model + .VertexModelConfigObject + .Auth + .Credentials .Type .SERVICE_ACCOUNT ) @@ -800,14 +1167,46 @@ internal class SessionServiceTest { .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() ) - .location("us-central1") - .project("my-gcp-project") - .provider(ModelConfig.Provider.OPENAI) .build() ) .selector("nav") 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) + } } From 991a894f6c93371eaf18a5878a41a4e27928adc8 Mon Sep 17 00:00:00 2001 From: samfinton Date: Fri, 29 May 2026 20:50:52 +0200 Subject: [PATCH 10/11] fix(java): update model config examples --- .../api/example/LocalBrowserPlaywrightExample.java | 3 +-- .../example/LocalServerMultiregionBrowserExample.java | 3 +-- .../src/main/java/com/stagehand/api/example/Main.java | 10 ++++++---- .../api/example/RemoteBrowserPlaywrightExample.java | 3 +-- 4 files changed, 9 insertions(+), 10 deletions(-) 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()) From 12a5b955639eec1152d81a6a818eda44a7d37c34 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 29 May 2026 18:51:22 +0000 Subject: [PATCH 11/11] release: 3.21.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 22 ++++++++++++++++++++++ README.md | 8 ++++---- build.gradle.kts | 2 +- 4 files changed, 28 insertions(+), 6 deletions(-) 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/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 7d3d74e..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 ``` 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 {