diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 10f3091..6b7b74c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.2.0" + ".": "0.3.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 36782ae..5dba58c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 7 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-705638ac8966569986bd9ebb7c9761bf0016909e9f2753e77ceabb12c8049511.yml -openapi_spec_hash: a8fbbcaa38e91c7f97313620b42d8d62 -config_hash: a35b56eb05306a0f02e83c11d57f975f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-f7d6b6489159f611a2bfdc267ce0a6fc0455bed1ffa0c310044baaa5d8381b9b.yml +openapi_spec_hash: cd88d8068abfde8382da0bed674e440c +config_hash: 5c69fb596588b8ace08203858518c149 diff --git a/CHANGELOG.md b/CHANGELOG.md index 04011d6..3a2ee1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## 0.3.0 (2025-12-18) + +Full Changelog: [v0.2.0...v0.3.0](https://github.com/browserbase/stagehand-java/compare/v0.2.0...v0.3.0) + +### Features + +* **api:** manual updates ([b6bdf1a](https://github.com/browserbase/stagehand-java/commit/b6bdf1a26a45556dcfee59b088983cd7e7839f43)) +* **api:** manual updates ([9b6860a](https://github.com/browserbase/stagehand-java/commit/9b6860a4f60d1a740fbb026eef291e40fb8e1845)) +* **api:** manual updates ([28f7978](https://github.com/browserbase/stagehand-java/commit/28f7978e1f040169dbb20aa68efb8e5ceb9a34d2)) +* **api:** manual updates ([07db616](https://github.com/browserbase/stagehand-java/commit/07db616b7209716efa346035d65f5d7bf616c992)) +* **api:** manual updates ([3cf2073](https://github.com/browserbase/stagehand-java/commit/3cf20739785134610aa7bcbdbce3215c3c3a9970)) +* **api:** manual updates ([adace53](https://github.com/browserbase/stagehand-java/commit/adace53fc3e4a0fb4b866172aec98519ec0f29d5)) +* **api:** manual updates ([dd65420](https://github.com/browserbase/stagehand-java/commit/dd65420fecb521fba36cbf2a623ce675973a27a0)) +* **api:** manual updates ([067c9e6](https://github.com/browserbase/stagehand-java/commit/067c9e644940fa3671665a902bc605b8e358473f)) +* **api:** manual updates ([d7aabe3](https://github.com/browserbase/stagehand-java/commit/d7aabe36f42d8344477f2f468bd3ba15837c0516)) +* **api:** manual updates ([13074f1](https://github.com/browserbase/stagehand-java/commit/13074f11bbc12057b60e89f37f850ca8f5bc7a83)) + ## 0.2.0 (2025-12-16) Full Changelog: [v0.1.0...v0.2.0](https://github.com/browserbase/stagehand-java/compare/v0.1.0...v0.2.0) diff --git a/README.md b/README.md index c383ecb..d87bf13 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/0.2.0) -[![javadoc](https://javadoc.io/badge2/com.browserbase.api/stagehand-java/0.2.0/javadoc.svg)](https://javadoc.io/doc/com.browserbase.api/stagehand-java/0.2.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/0.3.0) +[![javadoc](https://javadoc.io/badge2/com.browserbase.api/stagehand-java/0.3.0/javadoc.svg)](https://javadoc.io/doc/com.browserbase.api/stagehand-java/0.3.0) @@ -13,7 +13,7 @@ It is generated with [Stainless](https://www.stainless.com/). -The REST API documentation can be found on [docs.stagehand.dev](https://docs.stagehand.dev). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.browserbase.api/stagehand-java/0.2.0). +The REST API documentation can be found on [docs.stagehand.dev](https://docs.stagehand.dev). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.browserbase.api/stagehand-java/0.3.0). @@ -24,7 +24,7 @@ The REST API documentation can be found on [docs.stagehand.dev](https://docs.sta ### Gradle ```kotlin -implementation("com.browserbase.api:stagehand-java:0.2.0") +implementation("com.browserbase.api:stagehand-java:0.3.0") ``` ### Maven @@ -33,7 +33,7 @@ implementation("com.browserbase.api:stagehand-java:0.2.0") com.browserbase.api stagehand-java - 0.2.0 + 0.3.0 ``` @@ -56,7 +56,7 @@ import com.browserbase.api.models.sessions.SessionActResponse; StagehandClient client = StagehandOkHttpClient.fromEnv(); SessionActParams params = SessionActParams.builder() - .sessionId("00000000-your-session-id-000000000000") + .id("00000000-your-session-id-000000000000") .input("click the first link on the page") .build(); SessionActResponse response = client.sessions().act(params); @@ -162,7 +162,7 @@ import java.util.concurrent.CompletableFuture; StagehandClient client = StagehandOkHttpClient.fromEnv(); SessionActParams params = SessionActParams.builder() - .sessionId("00000000-your-session-id-000000000000") + .id("00000000-your-session-id-000000000000") .input("click the first link on the page") .build(); CompletableFuture response = client.async().sessions().act(params); @@ -182,7 +182,7 @@ import java.util.concurrent.CompletableFuture; StagehandClientAsync client = StagehandOkHttpClientAsync.fromEnv(); SessionActParams params = SessionActParams.builder() - .sessionId("00000000-your-session-id-000000000000") + .id("00000000-your-session-id-000000000000") .input("click the first link on the page") .build(); CompletableFuture response = client.sessions().act(params); @@ -203,8 +203,7 @@ import com.browserbase.api.models.sessions.SessionStartParams; import com.browserbase.api.models.sessions.SessionStartResponse; SessionStartParams params = SessionStartParams.builder() - .browserbaseApiKey("your Browserbase API key") - .browserbaseProjectId("your Browserbase Project ID") + .modelName("openai/gpt-5-nano") .build(); HttpResponseFor response = client.sessions().withRawResponse().start(params); @@ -237,6 +236,8 @@ The SDK throws custom unchecked exception types: | 5xx | [`InternalServerException`](stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/InternalServerException.kt) | | others | [`UnexpectedStatusCodeException`](stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/UnexpectedStatusCodeException.kt) | + [`SseException`](stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/SseException.kt) is thrown for errors encountered during [SSE streaming](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) after a successful initial HTTP response. + - [`StagehandIoException`](stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/StagehandIoException.kt): I/O networking errors. - [`StagehandRetryableException`](stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/StagehandRetryableException.kt): Generic error indicating a failure that could be retried by the client. @@ -505,8 +506,8 @@ import com.browserbase.api.core.JsonMissing; import com.browserbase.api.models.sessions.SessionActParams; SessionActParams params = SessionActParams.builder() - .input("click the sign in button") - .sessionId(JsonMissing.of()) + .input("Click the login button") + .id(JsonMissing.of()) .build(); ``` diff --git a/build.gradle.kts b/build.gradle.kts index 63a11b4..d06f0a9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ repositories { allprojects { group = "com.browserbase.api" - version = "0.2.0" // x-release-please-version + version = "0.3.0" // x-release-please-version } subprojects { 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 8955635..40010ee 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 @@ -7,6 +7,7 @@ import com.browserbase.api.client.StagehandClientImpl import com.browserbase.api.core.ClientOptions import com.browserbase.api.core.Sleeper import com.browserbase.api.core.Timeout +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.QueryParams @@ -16,6 +17,7 @@ import java.net.Proxy import java.time.Clock import java.time.Duration import java.util.Optional +import java.util.concurrent.Executor import javax.net.ssl.HostnameVerifier import javax.net.ssl.SSLSocketFactory import javax.net.ssl.X509TrustManager @@ -121,6 +123,17 @@ class StagehandOkHttpClient private constructor() { */ fun jsonMapper(jsonMapper: JsonMapper) = apply { clientOptions.jsonMapper(jsonMapper) } + /** + * The executor to use for running [AsyncStreamResponse.Handler] callbacks. + * + * Defaults to a dedicated cached thread pool. + * + * This class takes ownership of the executor and shuts it down, if possible, when closed. + */ + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + clientOptions.streamHandlerExecutor(streamHandlerExecutor) + } + /** * The interface to use for delaying execution, like during retries. * 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 2cb208d..33ce68f 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 @@ -7,6 +7,7 @@ import com.browserbase.api.client.StagehandClientAsyncImpl import com.browserbase.api.core.ClientOptions import com.browserbase.api.core.Sleeper import com.browserbase.api.core.Timeout +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.QueryParams @@ -16,6 +17,7 @@ import java.net.Proxy import java.time.Clock import java.time.Duration import java.util.Optional +import java.util.concurrent.Executor import javax.net.ssl.HostnameVerifier import javax.net.ssl.SSLSocketFactory import javax.net.ssl.X509TrustManager @@ -121,6 +123,17 @@ class StagehandOkHttpClientAsync private constructor() { */ fun jsonMapper(jsonMapper: JsonMapper) = apply { clientOptions.jsonMapper(jsonMapper) } + /** + * The executor to use for running [AsyncStreamResponse.Handler] callbacks. + * + * Defaults to a dedicated cached thread pool. + * + * This class takes ownership of the executor and shuts it down, if possible, when closed. + */ + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + clientOptions.streamHandlerExecutor(streamHandlerExecutor) + } + /** * The interface to use for delaying execution, like during retries. * 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 8692cb0..2da3f9a 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 @@ -2,6 +2,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.PhantomReachableClosingHttpClient @@ -11,6 +12,11 @@ import com.fasterxml.jackson.databind.json.JsonMapper import java.time.Clock import java.time.Duration import java.util.Optional +import java.util.concurrent.Executor +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.ThreadFactory +import java.util.concurrent.atomic.AtomicLong import kotlin.jvm.optionals.getOrNull /** A class representing the SDK client configuration. */ @@ -40,6 +46,14 @@ private constructor( * rarely needs to be overridden. */ @get:JvmName("jsonMapper") val jsonMapper: JsonMapper, + /** + * The executor to use for running [AsyncStreamResponse.Handler] callbacks. + * + * Defaults to a dedicated cached thread pool. + * + * This class takes ownership of the executor and shuts it down, if possible, when closed. + */ + @get:JvmName("streamHandlerExecutor") val streamHandlerExecutor: Executor, /** * The interface to use for delaying execution, like during retries. * @@ -147,6 +161,7 @@ private constructor( private var httpClient: HttpClient? = null private var checkJacksonVersionCompatibility: Boolean = true private var jsonMapper: JsonMapper = jsonMapper() + private var streamHandlerExecutor: Executor? = null private var sleeper: Sleeper? = null private var clock: Clock = Clock.systemUTC() private var baseUrl: String? = null @@ -164,6 +179,7 @@ private constructor( httpClient = clientOptions.originalHttpClient checkJacksonVersionCompatibility = clientOptions.checkJacksonVersionCompatibility jsonMapper = clientOptions.jsonMapper + streamHandlerExecutor = clientOptions.streamHandlerExecutor sleeper = clientOptions.sleeper clock = clientOptions.clock baseUrl = clientOptions.baseUrl @@ -207,6 +223,20 @@ private constructor( */ fun jsonMapper(jsonMapper: JsonMapper) = apply { this.jsonMapper = jsonMapper } + /** + * The executor to use for running [AsyncStreamResponse.Handler] callbacks. + * + * Defaults to a dedicated cached thread pool. + * + * This class takes ownership of the executor and shuts it down, if possible, when closed. + */ + fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { + this.streamHandlerExecutor = + if (streamHandlerExecutor is ExecutorService) + PhantomReachableExecutorService(streamHandlerExecutor) + else streamHandlerExecutor + } + /** * The interface to use for delaying execution, like during retries. * @@ -422,6 +452,24 @@ private constructor( */ fun build(): ClientOptions { val httpClient = checkRequired("httpClient", httpClient) + val streamHandlerExecutor = + streamHandlerExecutor + ?: PhantomReachableExecutorService( + Executors.newCachedThreadPool( + object : ThreadFactory { + + private val threadFactory: ThreadFactory = + Executors.defaultThreadFactory() + private val count = AtomicLong(0) + + override fun newThread(runnable: Runnable): Thread = + threadFactory.newThread(runnable).also { + it.name = + "stagehand-stream-handler-thread-${count.getAndIncrement()}" + } + } + ) + ) val sleeper = sleeper ?: PhantomReachableSleeper(DefaultSleeper()) val browserbaseApiKey = checkRequired("browserbaseApiKey", browserbaseApiKey) val browserbaseProjectId = checkRequired("browserbaseProjectId", browserbaseProjectId) @@ -464,6 +512,7 @@ private constructor( .build(), checkJacksonVersionCompatibility, jsonMapper, + streamHandlerExecutor, sleeper, clock, baseUrl, @@ -491,6 +540,7 @@ private constructor( */ fun close() { httpClient.close() + (streamHandlerExecutor as? ExecutorService)?.shutdown() sleeper.close() } } diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/handlers/SseHandler.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/handlers/SseHandler.kt new file mode 100644 index 0000000..039faae --- /dev/null +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/handlers/SseHandler.kt @@ -0,0 +1,137 @@ +// File generated from our OpenAPI spec by Stainless. + +@file:JvmName("SseHandler") + +package com.browserbase.api.core.handlers + +import com.browserbase.api.core.JsonMissing +import com.browserbase.api.core.http.HttpResponse +import com.browserbase.api.core.http.HttpResponse.Handler +import com.browserbase.api.core.http.SseMessage +import com.browserbase.api.core.http.StreamResponse +import com.browserbase.api.core.http.map +import com.browserbase.api.errors.SseException +import com.fasterxml.jackson.databind.json.JsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef + +@JvmSynthetic +internal fun sseHandler(jsonMapper: JsonMapper): Handler> = + streamHandler { response, lines -> + val state = SseState(jsonMapper) + var done = false + for (line in lines) { + // Stop emitting messages, but iterate through the full stream. + if (done) { + continue + } + + val message = state.decode(line) ?: continue + + when { + message.data.startsWith("finished") -> { + // In this case we don't break because we still want to iterate through the full + // stream. + done = true + continue + } + message.data.startsWith("error") -> { + throw SseException.builder() + .statusCode(response.statusCode()) + .headers(response.headers()) + .body( + try { + jsonMapper.readValue(message.data, jacksonTypeRef()) + } catch (e: Exception) { + JsonMissing.of() + } + ) + .build() + } + } + + if (message.event == null) { + yield(message) + } + } + } + +private class SseState( + val jsonMapper: JsonMapper, + var event: String? = null, + val data: MutableList = mutableListOf(), + var lastId: String? = null, + var retry: Int? = null, +) { + // https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation + fun decode(line: String): SseMessage? { + if (line.isEmpty()) { + return flush() + } + + if (line.startsWith(':')) { + return null + } + + val fieldName: String + var value: String + + val colonIndex = line.indexOf(':') + if (colonIndex == -1) { + fieldName = line + value = "" + } else { + fieldName = line.substring(0, colonIndex) + value = line.substring(colonIndex + 1) + } + + if (value.startsWith(' ')) { + value = value.substring(1) + } + + when (fieldName) { + "event" -> event = value + "data" -> data.add(value) + "id" -> { + if (!value.contains('\u0000')) { + lastId = value + } + } + "retry" -> value.toIntOrNull()?.let { retry = it } + } + + return null + } + + private fun flush(): SseMessage? { + if (isEmpty()) { + return null + } + + val message = + SseMessage.builder() + .jsonMapper(jsonMapper) + .event(event) + .data(data.joinToString("\n")) + .id(lastId) + .retry(retry) + .build() + + // NOTE: Per the SSE spec, do not reset lastId. + event = null + data.clear() + retry = null + + return message + } + + private fun isEmpty(): Boolean = + event.isNullOrEmpty() && data.isEmpty() && lastId.isNullOrEmpty() && retry == null +} + +@JvmSynthetic +internal inline fun Handler>.mapJson(): + Handler> = + object : Handler> { + override fun handle(response: HttpResponse): StreamResponse = + this@mapJson.handle(response).map { it.json() } + } diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/handlers/StreamHandler.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/handlers/StreamHandler.kt new file mode 100644 index 0000000..4cd4f25 --- /dev/null +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/handlers/StreamHandler.kt @@ -0,0 +1,102 @@ +@file:JvmName("StreamHandler") + +package com.browserbase.api.core.handlers + +import com.browserbase.api.core.http.HttpResponse +import com.browserbase.api.core.http.HttpResponse.Handler +import com.browserbase.api.core.http.PhantomReachableClosingStreamResponse +import com.browserbase.api.core.http.StreamResponse +import com.browserbase.api.errors.StagehandIoException +import java.io.IOException +import java.util.stream.Stream +import kotlin.streams.asStream + +@JvmSynthetic +internal fun streamHandler( + block: suspend SequenceScope.(response: HttpResponse, lines: Sequence) -> Unit +): Handler> = + object : Handler> { + + override fun handle(response: HttpResponse): StreamResponse { + val reader = response.body().bufferedReader() + val sequence = + // Wrap in a `CloseableSequence` to avoid performing a read on the `reader` + // after it has been closed, which would throw an `IOException`. + CloseableSequence( + sequence { + reader.useLines { lines -> + block( + response, + // We wrap the `lines` instead of the top-level sequence because + // we only want to catch `IOException` from the reader; not from + // the user's own code. + IOExceptionWrappingSequence(lines), + ) + } + } + .constrainOnce() + ) + + return PhantomReachableClosingStreamResponse( + object : StreamResponse { + + override fun stream(): Stream = sequence.asStream() + + override fun close() { + sequence.close() + reader.close() + response.close() + } + } + ) + } + } + +/** A sequence that catches, wraps, and rethrows [IOException] as [StagehandIoException]. */ +private class IOExceptionWrappingSequence(private val sequence: Sequence) : Sequence { + + override fun iterator(): Iterator { + val iterator = sequence.iterator() + return object : Iterator { + + override fun next(): T = + try { + iterator.next() + } catch (e: IOException) { + throw StagehandIoException("Stream failed", e) + } + + override fun hasNext(): Boolean = + try { + iterator.hasNext() + } catch (e: IOException) { + throw StagehandIoException("Stream failed", e) + } + } + } +} + +/** + * A sequence that can be closed. + * + * Once [close] is called, it will not yield more elements. It will also no longer consult the + * underlying [Iterator.hasNext] method. + */ +private class CloseableSequence(private val sequence: Sequence) : Sequence { + + private var isClosed: Boolean = false + + override fun iterator(): Iterator { + val iterator = sequence.iterator() + return object : Iterator { + + override fun next(): T = iterator.next() + + override fun hasNext(): Boolean = !isClosed && iterator.hasNext() + } + } + + fun close() { + isClosed = true + } +} diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/http/SseMessage.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/http/SseMessage.kt new file mode 100644 index 0000000..21cc44e --- /dev/null +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/http/SseMessage.kt @@ -0,0 +1,74 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.browserbase.api.core.http + +import com.browserbase.api.errors.StagehandInvalidDataException +import com.fasterxml.jackson.databind.json.JsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import java.util.Objects + +internal class SseMessage +private constructor( + val jsonMapper: JsonMapper, + val event: String?, + val data: String, + val id: String?, + val retry: Int?, +) { + + companion object { + @JvmStatic fun builder() = Builder() + } + + class Builder internal constructor() { + + private var jsonMapper: JsonMapper? = null + private var event: String? = null + private var data: String = "" + private var id: String? = null + private var retry: Int? = null + + fun jsonMapper(jsonMapper: JsonMapper) = apply { this.jsonMapper = jsonMapper } + + fun event(event: String?) = apply { this.event = event } + + fun data(data: String) = apply { this.data = data } + + fun id(id: String?) = apply { this.id = id } + + fun retry(retry: Int?) = apply { this.retry = retry } + + fun build(): SseMessage = SseMessage(jsonMapper!!, event, data, id, retry) + } + + inline fun json(): T = + try { + jsonMapper.readerFor(jacksonTypeRef()).readValue(jsonNode) + } catch (e: Exception) { + throw StagehandInvalidDataException("Error reading response", e) + } + + private val jsonNode by lazy { + try { + jsonMapper.readTree(data) + } catch (e: Exception) { + throw StagehandInvalidDataException("Error reading response", e) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SseMessage && + event == other.event && + data == other.data && + id == other.id && + retry == other.retry + } + + override fun hashCode(): Int = Objects.hash(event, data, id, retry) + + override fun toString(): String = "SseMessage{event=$event, data=$data, id=$id, retry=$retry}" +} diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/SseException.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/SseException.kt new file mode 100644 index 0000000..0dfdd2f --- /dev/null +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/SseException.kt @@ -0,0 +1,91 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.browserbase.api.errors + +import com.browserbase.api.core.JsonValue +import com.browserbase.api.core.checkRequired +import com.browserbase.api.core.http.Headers +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class SseException +private constructor( + private val statusCode: Int, + private val headers: Headers, + private val body: JsonValue, + cause: Throwable?, +) : StagehandServiceException("$statusCode: $body", cause) { + + override fun statusCode(): Int = statusCode + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [SseException]. + * + * The following fields are required: + * ```java + * .statusCode() + * .headers() + * .body() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [SseException]. */ + class Builder internal constructor() { + + private var statusCode: Int? = null + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + @JvmSynthetic + internal fun from(sseException: SseException) = apply { + statusCode = sseException.statusCode + headers = sseException.headers + body = sseException.body + cause = sseException.cause + } + + fun statusCode(statusCode: Int) = apply { this.statusCode = statusCode } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** Alias for calling [Builder.cause] with `cause.orElse(null)`. */ + fun cause(cause: Optional) = cause(cause.getOrNull()) + + /** + * Returns an immutable instance of [SseException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .statusCode() + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): SseException = + SseException( + checkRequired("statusCode", statusCode), + checkRequired("headers", headers), + checkRequired("body", body), + cause, + ) + } +} diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/Action.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/Action.kt index d30a40b..e9ae4bc 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/Action.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/Action.kt @@ -19,39 +19,28 @@ import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull +/** Action object returned by observe and used by act */ class Action @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val arguments: JsonField>, private val description: JsonField, - private val method: JsonField, private val selector: JsonField, - private val backendNodeId: JsonField, + private val arguments: JsonField>, + private val method: JsonField, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("arguments") - @ExcludeMissing - arguments: JsonField> = JsonMissing.of(), @JsonProperty("description") @ExcludeMissing description: JsonField = JsonMissing.of(), - @JsonProperty("method") @ExcludeMissing method: JsonField = JsonMissing.of(), @JsonProperty("selector") @ExcludeMissing selector: JsonField = JsonMissing.of(), - @JsonProperty("backendNodeId") + @JsonProperty("arguments") @ExcludeMissing - backendNodeId: JsonField = JsonMissing.of(), - ) : this(arguments, description, method, selector, backendNodeId, mutableMapOf()) - - /** - * Arguments for the method - * - * @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 arguments(): List = arguments.getRequired("arguments") + arguments: JsonField> = JsonMissing.of(), + @JsonProperty("method") @ExcludeMissing method: JsonField = JsonMissing.of(), + ) : this(description, selector, arguments, method, mutableMapOf()) /** * Human-readable description of the action @@ -62,15 +51,7 @@ private constructor( fun description(): String = description.getRequired("description") /** - * Method to execute (e.g., "click", "fill") - * - * @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 method(): String = method.getRequired("method") - - /** - * CSS or XPath selector for the element + * CSS selector or XPath for the element * * @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). @@ -78,19 +59,20 @@ private constructor( fun selector(): String = selector.getRequired("selector") /** - * CDP backend node ID + * Arguments to pass to the method * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ - fun backendNodeId(): Optional = backendNodeId.getOptional("backendNodeId") + fun arguments(): Optional> = arguments.getOptional("arguments") /** - * Returns the raw JSON value of [arguments]. + * The method to execute (click, fill, etc.) * - * Unlike [arguments], this method doesn't throw if the JSON field has an unexpected type. + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). */ - @JsonProperty("arguments") @ExcludeMissing fun _arguments(): JsonField> = arguments + fun method(): Optional = method.getOptional("method") /** * Returns the raw JSON value of [description]. @@ -100,27 +82,25 @@ private constructor( @JsonProperty("description") @ExcludeMissing fun _description(): JsonField = description /** - * Returns the raw JSON value of [method]. + * Returns the raw JSON value of [selector]. * - * Unlike [method], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [selector], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("method") @ExcludeMissing fun _method(): JsonField = method + @JsonProperty("selector") @ExcludeMissing fun _selector(): JsonField = selector /** - * Returns the raw JSON value of [selector]. + * Returns the raw JSON value of [arguments]. * - * Unlike [selector], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [arguments], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("selector") @ExcludeMissing fun _selector(): JsonField = selector + @JsonProperty("arguments") @ExcludeMissing fun _arguments(): JsonField> = arguments /** - * Returns the raw JSON value of [backendNodeId]. + * Returns the raw JSON value of [method]. * - * Unlike [backendNodeId], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [method], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("backendNodeId") - @ExcludeMissing - fun _backendNodeId(): JsonField = backendNodeId + @JsonProperty("method") @ExcludeMissing fun _method(): JsonField = method @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -141,9 +121,7 @@ private constructor( * * The following fields are required: * ```java - * .arguments() * .description() - * .method() * .selector() * ``` */ @@ -153,24 +131,45 @@ private constructor( /** A builder for [Action]. */ class Builder internal constructor() { - private var arguments: JsonField>? = null private var description: JsonField? = null - private var method: JsonField? = null private var selector: JsonField? = null - private var backendNodeId: JsonField = JsonMissing.of() + private var arguments: JsonField>? = null + private var method: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(action: Action) = apply { - arguments = action.arguments.map { it.toMutableList() } description = action.description - method = action.method selector = action.selector - backendNodeId = action.backendNodeId + arguments = action.arguments.map { it.toMutableList() } + method = action.method additionalProperties = action.additionalProperties.toMutableMap() } - /** Arguments for the method */ + /** Human-readable description of the action */ + fun description(description: String) = description(JsonField.of(description)) + + /** + * Sets [Builder.description] to an arbitrary JSON value. + * + * You should usually call [Builder.description] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun description(description: JsonField) = apply { this.description = description } + + /** CSS selector or XPath for the element */ + fun selector(selector: String) = selector(JsonField.of(selector)) + + /** + * Sets [Builder.selector] to an arbitrary JSON value. + * + * You should usually call [Builder.selector] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun selector(selector: JsonField) = apply { this.selector = selector } + + /** Arguments to pass to the method */ fun arguments(arguments: List) = arguments(JsonField.of(arguments)) /** @@ -196,19 +195,7 @@ private constructor( } } - /** Human-readable description of the action */ - fun description(description: String) = description(JsonField.of(description)) - - /** - * Sets [Builder.description] to an arbitrary JSON value. - * - * You should usually call [Builder.description] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun description(description: JsonField) = apply { this.description = description } - - /** Method to execute (e.g., "click", "fill") */ + /** The method to execute (click, fill, etc.) */ fun method(method: String) = method(JsonField.of(method)) /** @@ -219,31 +206,6 @@ private constructor( */ fun method(method: JsonField) = apply { this.method = method } - /** CSS or XPath selector for the element */ - fun selector(selector: String) = selector(JsonField.of(selector)) - - /** - * Sets [Builder.selector] to an arbitrary JSON value. - * - * You should usually call [Builder.selector] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun selector(selector: JsonField) = apply { this.selector = selector } - - /** CDP backend node ID */ - fun backendNodeId(backendNodeId: Long) = backendNodeId(JsonField.of(backendNodeId)) - - /** - * Sets [Builder.backendNodeId] to an arbitrary JSON value. - * - * You should usually call [Builder.backendNodeId] with a well-typed [Long] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun backendNodeId(backendNodeId: JsonField) = apply { - this.backendNodeId = backendNodeId - } - fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() putAllAdditionalProperties(additionalProperties) @@ -270,9 +232,7 @@ private constructor( * * The following fields are required: * ```java - * .arguments() * .description() - * .method() * .selector() * ``` * @@ -280,11 +240,10 @@ private constructor( */ fun build(): Action = Action( - checkRequired("arguments", arguments).map { it.toImmutable() }, checkRequired("description", description), - checkRequired("method", method), checkRequired("selector", selector), - backendNodeId, + (arguments ?: JsonMissing.of()).map { it.toImmutable() }, + method, additionalProperties.toMutableMap(), ) } @@ -296,11 +255,10 @@ private constructor( return@apply } - arguments() description() - method() selector() - backendNodeId() + arguments() + method() validated = true } @@ -319,11 +277,10 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (arguments.asKnown().getOrNull()?.size ?: 0) + - (if (description.asKnown().isPresent) 1 else 0) + - (if (method.asKnown().isPresent) 1 else 0) + + (if (description.asKnown().isPresent) 1 else 0) + (if (selector.asKnown().isPresent) 1 else 0) + - (if (backendNodeId.asKnown().isPresent) 1 else 0) + (arguments.asKnown().getOrNull()?.size ?: 0) + + (if (method.asKnown().isPresent) 1 else 0) override fun equals(other: Any?): Boolean { if (this === other) { @@ -331,20 +288,19 @@ private constructor( } return other is Action && - arguments == other.arguments && description == other.description && - method == other.method && selector == other.selector && - backendNodeId == other.backendNodeId && + arguments == other.arguments && + method == other.method && additionalProperties == other.additionalProperties } private val hashCode: Int by lazy { - Objects.hash(arguments, description, method, selector, backendNodeId, additionalProperties) + Objects.hash(description, selector, arguments, method, additionalProperties) } override fun hashCode(): Int = hashCode override fun toString() = - "Action{arguments=$arguments, description=$description, method=$method, selector=$selector, backendNodeId=$backendNodeId, additionalProperties=$additionalProperties}" + "Action{description=$description, selector=$selector, arguments=$arguments, method=$method, additionalProperties=$additionalProperties}" } 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 e08ebfa..e416a06 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,205 +2,72 @@ package com.browserbase.api.models.sessions -import com.browserbase.api.core.Enum +import com.browserbase.api.core.BaseDeserializer +import com.browserbase.api.core.BaseSerializer 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.errors.StagehandInvalidDataException import com.fasterxml.jackson.annotation.JsonAnyGetter import com.fasterxml.jackson.annotation.JsonAnySetter import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import java.util.Collections import java.util.Objects import java.util.Optional -import kotlin.jvm.optionals.getOrNull +/** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', 'anthropic/claude-4.5-opus') + */ +@JsonDeserialize(using = ModelConfig.Deserializer::class) +@JsonSerialize(using = ModelConfig.Serializer::class) class ModelConfig -@JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val apiKey: JsonField, - private val baseUrl: JsonField, - private val model: JsonField, - private val provider: JsonField, - private val additionalProperties: MutableMap, + private val name: String? = null, + private val modelConfigObject: ModelConfigObject? = null, + private val _json: JsonValue? = null, ) { - @JsonCreator - private constructor( - @JsonProperty("apiKey") @ExcludeMissing apiKey: JsonField = JsonMissing.of(), - @JsonProperty("baseURL") @ExcludeMissing baseUrl: JsonField = JsonMissing.of(), - @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), - @JsonProperty("provider") @ExcludeMissing provider: JsonField = JsonMissing.of(), - ) : this(apiKey, baseUrl, model, provider, mutableMapOf()) - /** - * 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). + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') */ - fun apiKey(): Optional = apiKey.getOptional("apiKey") + fun name(): Optional = Optional.ofNullable(name) - /** - * Custom base URL for API - * - * @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 modelConfigObject(): Optional = Optional.ofNullable(modelConfigObject) - /** - * Model name - * - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected value). - */ - fun model(): Optional = model.getOptional("model") + fun isName(): Boolean = name != null - /** - * @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") + fun isModelConfigObject(): Boolean = modelConfigObject != null /** - * Returns the raw JSON value of [apiKey]. - * - * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected type. + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') */ - @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 [model]. - * - * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model - - /** - * Returns the raw JSON value of [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 [ModelConfig]. */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [ModelConfig]. */ - class Builder internal constructor() { - - private var apiKey: JsonField = JsonMissing.of() - private var baseUrl: JsonField = JsonMissing.of() - private var model: JsonField = JsonMissing.of() - private var provider: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(modelConfig: ModelConfig) = apply { - apiKey = modelConfig.apiKey - baseUrl = modelConfig.baseUrl - model = modelConfig.model - provider = modelConfig.provider - additionalProperties = modelConfig.additionalProperties.toMutableMap() - } + fun asName(): String = name.getOrThrow("name") - /** API key for the model provider */ - fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + fun asModelConfigObject(): ModelConfigObject = modelConfigObject.getOrThrow("modelConfigObject") - /** - * 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 } - - /** Custom base URL for API */ - 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 } + fun _json(): Optional = Optional.ofNullable(_json) - /** Model name */ - fun model(model: String) = model(JsonField.of(model)) - - /** - * Sets [Builder.model] to an arbitrary JSON value. - * - * You should usually call [Builder.model] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun model(model: JsonField) = apply { this.model = model } - - 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 accept(visitor: Visitor): T = + when { + name != null -> visitor.visitName(name) + modelConfigObject != null -> visitor.visitModelConfigObject(modelConfigObject) + else -> visitor.unknown(_json) } - 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 [ModelConfig]. - * - * Further updates to this [Builder] will not mutate the returned instance. - */ - fun build(): ModelConfig = - ModelConfig(apiKey, baseUrl, model, provider, additionalProperties.toMutableMap()) - } - private var validated: Boolean = false fun validate(): ModelConfig = apply { @@ -208,10 +75,15 @@ private constructor( return@apply } - apiKey() - baseUrl() - model() - provider().ifPresent { it.validate() } + accept( + object : Visitor { + override fun visitName(name: String) {} + + override fun visitModelConfigObject(modelConfigObject: ModelConfigObject) { + modelConfigObject.validate() + } + } + ) validated = true } @@ -230,112 +102,312 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (if (apiKey.asKnown().isPresent) 1 else 0) + - (if (baseUrl.asKnown().isPresent) 1 else 0) + - (if (model.asKnown().isPresent) 1 else 0) + - (provider.asKnown().getOrNull()?.validity() ?: 0) + accept( + object : Visitor { + override fun visitName(name: String) = 1 + + override fun visitModelConfigObject(modelConfigObject: ModelConfigObject) = + modelConfigObject.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ModelConfig && + name == other.name && + modelConfigObject == other.modelConfigObject + } - class Provider @JsonCreator private constructor(private val value: JsonField) : Enum { + override fun hashCode(): Int = Objects.hash(name, modelConfigObject) + + override fun toString(): String = + when { + name != null -> "ModelConfig{name=$name}" + modelConfigObject != null -> "ModelConfig{modelConfigObject=$modelConfigObject}" + _json != null -> "ModelConfig{_unknown=$_json}" + else -> throw IllegalStateException("Invalid ModelConfig") + } + + companion object { /** - * 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. + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') */ - @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + @JvmStatic fun ofName(name: String) = ModelConfig(name = name) - companion object { + @JvmStatic + fun ofModelConfigObject(modelConfigObject: ModelConfigObject) = + ModelConfig(modelConfigObject = modelConfigObject) + } - @JvmField val OPENAI = of("openai") + /** + * An interface that defines how to map each variant of [ModelConfig] to a value of type [T]. + */ + interface Visitor { - @JvmField val ANTHROPIC = of("anthropic") + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') + */ + fun visitName(name: String): T - @JvmField val GOOGLE = of("google") + fun visitModelConfigObject(modelConfigObject: ModelConfigObject): T - @JvmStatic fun of(value: String) = Provider(JsonField.of(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. + * + * @throws StagehandInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw StagehandInvalidDataException("Unknown ModelConfig: $json") } + } - /** An enum containing [Provider]'s known values. */ - enum class Known { - OPENAI, - ANTHROPIC, - GOOGLE, + internal class Deserializer : BaseDeserializer(ModelConfig::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): ModelConfig { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + ModelConfig(modelConfigObject = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ModelConfig(name = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with all + // the possible variants (e.g. deserializing from array). + 0 -> ModelConfig(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(ModelConfig::class) { + + override fun serialize( + value: ModelConfig, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.name != null -> generator.writeObject(value.name) + value.modelConfigObject != null -> generator.writeObject(value.modelConfigObject) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid ModelConfig") + } } + } + + class ModelConfigObject + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val modelName: JsonField, + private val apiKey: JsonField, + private val baseUrl: 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(), + ) : this(modelName, apiKey, baseUrl, mutableMapOf()) /** - * An enum containing [Provider]'s known values, as well as an [_UNKNOWN] member. + * Model name string without prefix (e.g., 'gpt-5-nano', 'claude-4.5-opus') * - * 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. + * @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). */ - enum class Value { - OPENAI, - ANTHROPIC, - GOOGLE, - /** An enum member indicating that [Provider] was instantiated with an unknown value. */ - _UNKNOWN, - } + fun modelName(): String = modelName.getRequired("modelName") /** - * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] - * if the class was instantiated with an unknown value. + * API key for the model provider * - * 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) { - OPENAI -> Value.OPENAI - ANTHROPIC -> Value.ANTHROPIC - GOOGLE -> Value.GOOGLE - else -> Value._UNKNOWN - } + fun apiKey(): Optional = apiKey.getOptional("apiKey") /** - * Returns an enum member corresponding to this class instance's value. + * Base URL for the model provider * - * 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 the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") + + /** + * Returns the raw JSON value of [modelName]. * - * @throws StagehandInvalidDataException if this class instance's value is a not a known - * member. + * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected type. */ - fun known(): Known = - when (this) { - OPENAI -> Known.OPENAI - ANTHROPIC -> Known.ANTHROPIC - GOOGLE -> Known.GOOGLE - else -> throw StagehandInvalidDataException("Unknown Provider: $value") - } + @JsonProperty("modelName") @ExcludeMissing fun _modelName(): JsonField = modelName /** - * Returns this class instance's primitive wire representation. + * Returns the raw JSON value of [apiKey]. * - * This differs from the [toString] method because that method is primarily for debugging - * and generally doesn't throw. + * 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]. * - * @throws StagehandInvalidDataException if this class instance's value does not have the - * expected primitive type. + * Unlike [baseUrl], 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("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + + @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 [ModelConfigObject]. + * + * The following fields are required: + * ```java + * .modelName() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ModelConfigObject]. */ + class Builder internal constructor() { + + private var modelName: JsonField? = null + private var apiKey: JsonField = JsonMissing.of() + private var baseUrl: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(modelConfigObject: ModelConfigObject) = apply { + modelName = modelConfigObject.modelName + apiKey = modelConfigObject.apiKey + baseUrl = modelConfigObject.baseUrl + additionalProperties = modelConfigObject.additionalProperties.toMutableMap() + } + + /** Model name string without prefix (e.g., 'gpt-5-nano', 'claude-4.5-opus') */ + 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 } + + 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 [ModelConfigObject]. + * + * 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(): ModelConfigObject = + ModelConfigObject( + checkRequired("modelName", modelName), + apiKey, + baseUrl, + additionalProperties.toMutableMap(), + ) + } + private var validated: Boolean = false - fun validate(): Provider = apply { + fun validate(): ModelConfigObject = apply { if (validated) { return@apply } - known() + modelName() + apiKey() + baseUrl() validated = true } @@ -353,40 +425,31 @@ private constructor( * * Used for best match union deserialization. */ - @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + @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) override fun equals(other: Any?): Boolean { if (this === other) { return true } - return other is Provider && value == other.value + return other is ModelConfigObject && + modelName == other.modelName && + apiKey == other.apiKey && + baseUrl == other.baseUrl && + 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, additionalProperties) } - return other is ModelConfig && - apiKey == other.apiKey && - baseUrl == other.baseUrl && - model == other.model && - provider == other.provider && - additionalProperties == other.additionalProperties - } + override fun hashCode(): Int = hashCode - private val hashCode: Int by lazy { - Objects.hash(apiKey, baseUrl, model, provider, additionalProperties) + override fun toString() = + "ModelConfigObject{modelName=$modelName, apiKey=$apiKey, baseUrl=$baseUrl, additionalProperties=$additionalProperties}" } - - override fun hashCode(): Int = hashCode - - override fun toString() = - "ModelConfig{apiKey=$apiKey, baseUrl=$baseUrl, model=$model, 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 34cae33..a37780c 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 @@ -28,30 +28,43 @@ 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.time.OffsetDateTime +import java.time.format.DateTimeFormatter import java.util.Collections import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** - * Performs a browser action based on natural language instruction or a specific action object - * returned by observe(). - */ +/** Executes a browser action using natural language instructions or a predefined Action object. */ class SessionActParams private constructor( - private val sessionId: String?, + private val id: String?, + private val xLanguage: XLanguage?, + private val xSdkVersion: String?, + private val xSentAt: OffsetDateTime?, private val xStreamResponse: XStreamResponse?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun sessionId(): Optional = Optional.ofNullable(sessionId) + /** Unique session identifier */ + fun id(): Optional = Optional.ofNullable(id) + /** Client SDK language */ + fun xLanguage(): Optional = Optional.ofNullable(xLanguage) + + /** Version of the Stagehand SDK */ + fun xSdkVersion(): Optional = Optional.ofNullable(xSdkVersion) + + /** ISO timestamp when request was sent */ + fun xSentAt(): Optional = Optional.ofNullable(xSentAt) + + /** Whether to stream the response via SSE */ fun xStreamResponse(): Optional = Optional.ofNullable(xStreamResponse) /** - * Natural language instruction + * Natural language instruction or Action 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). @@ -59,7 +72,7 @@ private constructor( fun input(): Input = body.input() /** - * Frame ID to act on (optional) + * Target frame ID for the action * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -119,7 +132,10 @@ private constructor( /** A builder for [SessionActParams]. */ class Builder internal constructor() { - private var sessionId: String? = null + private var id: String? = null + private var xLanguage: XLanguage? = null + private var xSdkVersion: String? = null + private var xSentAt: OffsetDateTime? = null private var xStreamResponse: XStreamResponse? = null private var body: Body.Builder = Body.builder() private var additionalHeaders: Headers.Builder = Headers.builder() @@ -127,18 +143,41 @@ private constructor( @JvmSynthetic internal fun from(sessionActParams: SessionActParams) = apply { - sessionId = sessionActParams.sessionId + id = sessionActParams.id + xLanguage = sessionActParams.xLanguage + xSdkVersion = sessionActParams.xSdkVersion + xSentAt = sessionActParams.xSentAt xStreamResponse = sessionActParams.xStreamResponse body = sessionActParams.body.toBuilder() additionalHeaders = sessionActParams.additionalHeaders.toBuilder() additionalQueryParams = sessionActParams.additionalQueryParams.toBuilder() } - fun sessionId(sessionId: String?) = apply { this.sessionId = sessionId } + /** Unique session identifier */ + fun id(id: String?) = apply { this.id = id } + + /** Alias for calling [Builder.id] with `id.orElse(null)`. */ + fun id(id: Optional) = id(id.getOrNull()) + + /** Client SDK language */ + fun xLanguage(xLanguage: XLanguage?) = apply { this.xLanguage = xLanguage } + + /** Alias for calling [Builder.xLanguage] with `xLanguage.orElse(null)`. */ + fun xLanguage(xLanguage: Optional) = xLanguage(xLanguage.getOrNull()) + + /** Version of the Stagehand SDK */ + fun xSdkVersion(xSdkVersion: String?) = apply { this.xSdkVersion = xSdkVersion } + + /** Alias for calling [Builder.xSdkVersion] with `xSdkVersion.orElse(null)`. */ + fun xSdkVersion(xSdkVersion: Optional) = xSdkVersion(xSdkVersion.getOrNull()) - /** Alias for calling [Builder.sessionId] with `sessionId.orElse(null)`. */ - fun sessionId(sessionId: Optional) = sessionId(sessionId.getOrNull()) + /** ISO timestamp when request was sent */ + fun xSentAt(xSentAt: OffsetDateTime?) = apply { this.xSentAt = xSentAt } + /** Alias for calling [Builder.xSentAt] with `xSentAt.orElse(null)`. */ + fun xSentAt(xSentAt: Optional) = xSentAt(xSentAt.getOrNull()) + + /** Whether to stream the response via SSE */ fun xStreamResponse(xStreamResponse: XStreamResponse?) = apply { this.xStreamResponse = xStreamResponse } @@ -158,7 +197,7 @@ private constructor( */ fun body(body: Body) = apply { this.body = body.toBuilder() } - /** Natural language instruction */ + /** Natural language instruction or Action object */ fun input(input: Input) = apply { body.input(input) } /** @@ -175,7 +214,7 @@ private constructor( /** Alias for calling [input] with `Input.ofAction(action)`. */ fun input(action: Action) = apply { body.input(action) } - /** Frame ID to act on (optional) */ + /** Target frame ID for the action */ fun frameId(frameId: String) = apply { body.frameId(frameId) } /** @@ -327,7 +366,10 @@ private constructor( */ fun build(): SessionActParams = SessionActParams( - sessionId, + id, + xLanguage, + xSdkVersion, + xSentAt, xStreamResponse, body.build(), additionalHeaders.build(), @@ -339,13 +381,16 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> sessionId ?: "" + 0 -> id ?: "" else -> "" } override fun _headers(): Headers = Headers.builder() .apply { + xLanguage?.let { put("x-language", it.toString()) } + xSdkVersion?.let { put("x-sdk-version", it) } + xSentAt?.let { put("x-sent-at", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) } xStreamResponse?.let { put("x-stream-response", it.toString()) } putAll(additionalHeaders) } @@ -370,7 +415,7 @@ private constructor( ) : this(input, frameId, options, mutableMapOf()) /** - * Natural language instruction + * Natural language instruction or Action 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). @@ -378,7 +423,7 @@ private constructor( fun input(): Input = input.getRequired("input") /** - * Frame ID to act on (optional) + * Target frame ID for the action * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). @@ -453,7 +498,7 @@ private constructor( additionalProperties = body.additionalProperties.toMutableMap() } - /** Natural language instruction */ + /** Natural language instruction or Action object */ fun input(input: Input) = input(JsonField.of(input)) /** @@ -471,7 +516,7 @@ private constructor( /** Alias for calling [input] with `Input.ofAction(action)`. */ fun input(action: Action) = input(Input.ofAction(action)) - /** Frame ID to act on (optional) */ + /** Target frame ID for the action */ fun frameId(frameId: String) = frameId(JsonField.of(frameId)) /** @@ -589,7 +634,7 @@ private constructor( "Body{input=$input, frameId=$frameId, options=$options, additionalProperties=$additionalProperties}" } - /** Natural language instruction */ + /** Natural language instruction or Action object */ @JsonDeserialize(using = Input.Deserializer::class) @JsonSerialize(using = Input.Serializer::class) class Input @@ -599,18 +644,18 @@ private constructor( private val _json: JsonValue? = null, ) { - /** Natural language instruction */ fun string(): Optional = Optional.ofNullable(string) + /** Action object returned by observe and used by act */ fun action(): Optional = Optional.ofNullable(action) fun isString(): Boolean = string != null fun isAction(): Boolean = action != null - /** Natural language instruction */ fun asString(): String = string.getOrThrow("string") + /** Action object returned by observe and used by act */ fun asAction(): Action = action.getOrThrow("action") fun _json(): Optional = Optional.ofNullable(_json) @@ -687,18 +732,18 @@ private constructor( companion object { - /** Natural language instruction */ @JvmStatic fun ofString(string: String) = Input(string = string) + /** Action object returned by observe and used by act */ @JvmStatic fun ofAction(action: Action) = Input(action = action) } /** An interface that defines how to map each variant of [Input] to a value of type [T]. */ interface Visitor { - /** Natural language instruction */ fun visitString(string: String): T + /** Action object returned by observe and used by act */ fun visitAction(action: Action): T /** @@ -767,7 +812,7 @@ private constructor( @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val model: JsonField, - private val timeout: JsonField, + private val timeout: JsonField, private val variables: JsonField, private val additionalProperties: MutableMap, ) { @@ -775,28 +820,31 @@ private constructor( @JsonCreator private constructor( @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), - @JsonProperty("timeout") @ExcludeMissing timeout: JsonField = JsonMissing.of(), + @JsonProperty("timeout") @ExcludeMissing timeout: JsonField = JsonMissing.of(), @JsonProperty("variables") @ExcludeMissing variables: JsonField = JsonMissing.of(), ) : this(model, timeout, variables, mutableMapOf()) /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ fun model(): Optional = model.getOptional("model") /** - * Timeout in milliseconds + * Timeout in ms for the action * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun timeout(): Optional = timeout.getOptional("timeout") + fun timeout(): Optional = timeout.getOptional("timeout") /** - * Template variables for instruction + * Variables to substitute in the action instruction * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). @@ -815,7 +863,7 @@ private constructor( * * Unlike [timeout], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("timeout") @ExcludeMissing fun _timeout(): JsonField = timeout + @JsonProperty("timeout") @ExcludeMissing fun _timeout(): JsonField = timeout /** * Returns the raw JSON value of [variables]. @@ -848,7 +896,7 @@ private constructor( class Builder internal constructor() { private var model: JsonField = JsonMissing.of() - private var timeout: JsonField = JsonMissing.of() + private var timeout: JsonField = JsonMissing.of() private var variables: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -860,6 +908,10 @@ private constructor( additionalProperties = options.additionalProperties.toMutableMap() } + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') + */ fun model(model: ModelConfig) = model(JsonField.of(model)) /** @@ -871,19 +923,28 @@ private constructor( */ fun model(model: JsonField) = apply { this.model = model } - /** Timeout in milliseconds */ - fun timeout(timeout: Long) = timeout(JsonField.of(timeout)) + /** Alias for calling [model] with `ModelConfig.ofName(name)`. */ + fun model(name: String) = model(ModelConfig.ofName(name)) + + /** + * Alias for calling [model] with `ModelConfig.ofModelConfigObject(modelConfigObject)`. + */ + fun model(modelConfigObject: ModelConfig.ModelConfigObject) = + model(ModelConfig.ofModelConfigObject(modelConfigObject)) + + /** Timeout in ms for the action */ + fun timeout(timeout: Double) = timeout(JsonField.of(timeout)) /** * Sets [Builder.timeout] to an arbitrary JSON value. * - * You should usually call [Builder.timeout] with a well-typed [Long] value instead. + * You should usually call [Builder.timeout] with a well-typed [Double] value instead. * This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun timeout(timeout: JsonField) = apply { this.timeout = timeout } + fun timeout(timeout: JsonField) = apply { this.timeout = timeout } - /** Template variables for instruction */ + /** Variables to substitute in the action instruction */ fun variables(variables: Variables) = variables(JsonField.of(variables)) /** @@ -956,7 +1017,7 @@ private constructor( (if (timeout.asKnown().isPresent) 1 else 0) + (variables.asKnown().getOrNull()?.validity() ?: 0) - /** Template variables for instruction */ + /** Variables to substitute in the action instruction */ class Variables @JsonCreator private constructor( @@ -1081,6 +1142,143 @@ private constructor( "Options{model=$model, timeout=$timeout, variables=$variables, additionalProperties=$additionalProperties}" } + /** Client SDK language */ + class XLanguage @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 TYPESCRIPT = of("typescript") + + @JvmField val PYTHON = of("python") + + @JvmField val PLAYGROUND = of("playground") + + @JvmStatic fun of(value: String) = XLanguage(JsonField.of(value)) + } + + /** An enum containing [XLanguage]'s known values. */ + enum class Known { + TYPESCRIPT, + PYTHON, + PLAYGROUND, + } + + /** + * An enum containing [XLanguage]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [XLanguage] 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 { + TYPESCRIPT, + PYTHON, + PLAYGROUND, + /** + * An enum member indicating that [XLanguage] 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) { + TYPESCRIPT -> Value.TYPESCRIPT + PYTHON -> Value.PYTHON + PLAYGROUND -> Value.PLAYGROUND + 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) { + TYPESCRIPT -> Known.TYPESCRIPT + PYTHON -> Known.PYTHON + PLAYGROUND -> Known.PLAYGROUND + else -> throw StagehandInvalidDataException("Unknown XLanguage: $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 + + fun validate(): XLanguage = 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 XLanguage && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Whether to stream the response via SSE */ class XStreamResponse @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -1218,7 +1416,10 @@ private constructor( } return other is SessionActParams && - sessionId == other.sessionId && + id == other.id && + xLanguage == other.xLanguage && + xSdkVersion == other.xSdkVersion && + xSentAt == other.xSentAt && xStreamResponse == other.xStreamResponse && body == other.body && additionalHeaders == other.additionalHeaders && @@ -1226,8 +1427,17 @@ private constructor( } override fun hashCode(): Int = - Objects.hash(sessionId, xStreamResponse, body, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + xLanguage, + xSdkVersion, + xSentAt, + xStreamResponse, + body, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "SessionActParams{sessionId=$sessionId, xStreamResponse=$xStreamResponse, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "SessionActParams{id=$id, xLanguage=$xLanguage, xSdkVersion=$xSdkVersion, xSentAt=$xSentAt, xStreamResponse=$xStreamResponse, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActResponse.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActResponse.kt index 69dfcab..db27614 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActResponse.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActResponse.kt @@ -16,44 +16,31 @@ import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty import java.util.Collections import java.util.Objects +import java.util.Optional import kotlin.jvm.optionals.getOrNull class SessionActResponse @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val actions: JsonField>, - private val message: JsonField, + private val data: JsonField, private val success: JsonField, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("actions") - @ExcludeMissing - actions: JsonField> = JsonMissing.of(), - @JsonProperty("message") @ExcludeMissing message: JsonField = JsonMissing.of(), + @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(), @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(), - ) : this(actions, message, success, mutableMapOf()) - - /** - * Actions that were executed - * - * @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 actions(): List = actions.getRequired("actions") + ) : this(data, success, mutableMapOf()) /** - * Result message - * * @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 message(): String = message.getRequired("message") + fun data(): Data = data.getRequired("data") /** - * Whether the action succeeded + * Indicates whether the request was successful * * @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). @@ -61,18 +48,11 @@ private constructor( fun success(): Boolean = success.getRequired("success") /** - * Returns the raw JSON value of [actions]. - * - * Unlike [actions], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("actions") @ExcludeMissing fun _actions(): JsonField> = actions - - /** - * Returns the raw JSON value of [message]. + * Returns the raw JSON value of [data]. * - * Unlike [message], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("message") @ExcludeMissing fun _message(): JsonField = message + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField = data /** * Returns the raw JSON value of [success]. @@ -100,8 +80,7 @@ private constructor( * * The following fields are required: * ```java - * .actions() - * .message() + * .data() * .success() * ``` */ @@ -111,57 +90,28 @@ private constructor( /** A builder for [SessionActResponse]. */ class Builder internal constructor() { - private var actions: JsonField>? = null - private var message: JsonField? = null + private var data: JsonField? = null private var success: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(sessionActResponse: SessionActResponse) = apply { - actions = sessionActResponse.actions.map { it.toMutableList() } - message = sessionActResponse.message + data = sessionActResponse.data success = sessionActResponse.success additionalProperties = sessionActResponse.additionalProperties.toMutableMap() } - /** Actions that were executed */ - fun actions(actions: List) = actions(JsonField.of(actions)) - - /** - * Sets [Builder.actions] to an arbitrary JSON value. - * - * You should usually call [Builder.actions] with a well-typed `List` value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun actions(actions: JsonField>) = apply { - this.actions = actions.map { it.toMutableList() } - } - - /** - * Adds a single [Action] to [actions]. - * - * @throws IllegalStateException if the field was previously set to a non-list. - */ - fun addAction(action: Action) = apply { - actions = - (actions ?: JsonField.of(mutableListOf())).also { - checkKnown("actions", it).add(action) - } - } - - /** Result message */ - fun message(message: String) = message(JsonField.of(message)) + fun data(data: Data) = data(JsonField.of(data)) /** - * Sets [Builder.message] to an arbitrary JSON value. + * Sets [Builder.data] to an arbitrary JSON value. * - * You should usually call [Builder.message] with a well-typed [String] value instead. This + * You should usually call [Builder.data] with a well-typed [Data] value instead. This * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun message(message: JsonField) = apply { this.message = message } + fun data(data: JsonField) = apply { this.data = data } - /** Whether the action succeeded */ + /** Indicates whether the request was successful */ fun success(success: Boolean) = success(JsonField.of(success)) /** @@ -198,8 +148,7 @@ private constructor( * * The following fields are required: * ```java - * .actions() - * .message() + * .data() * .success() * ``` * @@ -207,8 +156,7 @@ private constructor( */ fun build(): SessionActResponse = SessionActResponse( - checkRequired("actions", actions).map { it.toImmutable() }, - checkRequired("message", message), + checkRequired("data", data), checkRequired("success", success), additionalProperties.toMutableMap(), ) @@ -221,8 +169,7 @@ private constructor( return@apply } - actions().forEach { it.validate() } - message() + data().validate() success() validated = true } @@ -242,9 +189,507 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (actions.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + - (if (message.asKnown().isPresent) 1 else 0) + - (if (success.asKnown().isPresent) 1 else 0) + (data.asKnown().getOrNull()?.validity() ?: 0) + (if (success.asKnown().isPresent) 1 else 0) + + class Data + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val result: JsonField, + private val actionId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("result") @ExcludeMissing result: JsonField = JsonMissing.of(), + @JsonProperty("actionId") @ExcludeMissing actionId: JsonField = JsonMissing.of(), + ) : this(result, actionId, 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 result(): Result = result.getRequired("result") + + /** + * Action ID for tracking + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun actionId(): Optional = actionId.getOptional("actionId") + + /** + * Returns the raw JSON value of [result]. + * + * Unlike [result], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("result") @ExcludeMissing fun _result(): JsonField = result + + /** + * Returns the raw JSON value of [actionId]. + * + * Unlike [actionId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("actionId") @ExcludeMissing fun _actionId(): JsonField = actionId + + @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 [Data]. + * + * The following fields are required: + * ```java + * .result() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Data]. */ + class Builder internal constructor() { + + private var result: JsonField? = null + private var actionId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(data: Data) = apply { + result = data.result + actionId = data.actionId + additionalProperties = data.additionalProperties.toMutableMap() + } + + fun result(result: Result) = result(JsonField.of(result)) + + /** + * Sets [Builder.result] to an arbitrary JSON value. + * + * You should usually call [Builder.result] with a well-typed [Result] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun result(result: JsonField) = apply { this.result = result } + + /** Action ID for tracking */ + fun actionId(actionId: String) = actionId(JsonField.of(actionId)) + + /** + * Sets [Builder.actionId] to an arbitrary JSON value. + * + * You should usually call [Builder.actionId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun actionId(actionId: JsonField) = apply { this.actionId = actionId } + + 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 [Data]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .result() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Data = + Data(checkRequired("result", result), actionId, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Data = apply { + if (validated) { + return@apply + } + + result().validate() + actionId() + 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 = + (result.asKnown().getOrNull()?.validity() ?: 0) + + (if (actionId.asKnown().isPresent) 1 else 0) + + class Result + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val actionDescription: JsonField, + private val actions: JsonField>, + private val message: JsonField, + private val success: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("actionDescription") + @ExcludeMissing + actionDescription: JsonField = JsonMissing.of(), + @JsonProperty("actions") + @ExcludeMissing + actions: JsonField> = JsonMissing.of(), + @JsonProperty("message") + @ExcludeMissing + message: JsonField = JsonMissing.of(), + @JsonProperty("success") + @ExcludeMissing + success: JsonField = JsonMissing.of(), + ) : this(actionDescription, actions, message, success, mutableMapOf()) + + /** + * Description of the action that was performed + * + * @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 actionDescription(): String = actionDescription.getRequired("actionDescription") + + /** + * List of actions that were executed + * + * @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 actions(): List = actions.getRequired("actions") + + /** + * Human-readable result message + * + * @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 message(): String = message.getRequired("message") + + /** + * Whether the action completed successfully + * + * @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 success(): Boolean = success.getRequired("success") + + /** + * Returns the raw JSON value of [actionDescription]. + * + * Unlike [actionDescription], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("actionDescription") + @ExcludeMissing + fun _actionDescription(): JsonField = actionDescription + + /** + * Returns the raw JSON value of [actions]. + * + * Unlike [actions], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("actions") + @ExcludeMissing + fun _actions(): JsonField> = actions + + /** + * Returns the raw JSON value of [message]. + * + * Unlike [message], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("message") @ExcludeMissing fun _message(): JsonField = message + + /** + * Returns the raw JSON value of [success]. + * + * Unlike [success], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success + + @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 [Result]. + * + * The following fields are required: + * ```java + * .actionDescription() + * .actions() + * .message() + * .success() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Result]. */ + class Builder internal constructor() { + + private var actionDescription: JsonField? = null + private var actions: JsonField>? = null + private var message: JsonField? = null + private var success: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(result: Result) = apply { + actionDescription = result.actionDescription + actions = result.actions.map { it.toMutableList() } + message = result.message + success = result.success + additionalProperties = result.additionalProperties.toMutableMap() + } + + /** Description of the action that was performed */ + fun actionDescription(actionDescription: String) = + actionDescription(JsonField.of(actionDescription)) + + /** + * Sets [Builder.actionDescription] to an arbitrary JSON value. + * + * You should usually call [Builder.actionDescription] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun actionDescription(actionDescription: JsonField) = apply { + this.actionDescription = actionDescription + } + + /** List of actions that were executed */ + fun actions(actions: List) = actions(JsonField.of(actions)) + + /** + * Sets [Builder.actions] to an arbitrary JSON value. + * + * You should usually call [Builder.actions] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun actions(actions: JsonField>) = apply { + this.actions = actions.map { it.toMutableList() } + } + + /** + * Adds a single [Action] to [actions]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addAction(action: Action) = apply { + actions = + (actions ?: JsonField.of(mutableListOf())).also { + checkKnown("actions", it).add(action) + } + } + + /** Human-readable result message */ + fun message(message: String) = message(JsonField.of(message)) + + /** + * Sets [Builder.message] to an arbitrary JSON value. + * + * You should usually call [Builder.message] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun message(message: JsonField) = apply { this.message = message } + + /** Whether the action completed successfully */ + fun success(success: Boolean) = success(JsonField.of(success)) + + /** + * Sets [Builder.success] to an arbitrary JSON value. + * + * You should usually call [Builder.success] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun success(success: JsonField) = apply { this.success = success } + + 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 [Result]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .actionDescription() + * .actions() + * .message() + * .success() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Result = + Result( + checkRequired("actionDescription", actionDescription), + checkRequired("actions", actions).map { it.toImmutable() }, + checkRequired("message", message), + checkRequired("success", success), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Result = apply { + if (validated) { + return@apply + } + + actionDescription() + actions().forEach { it.validate() } + message() + success() + 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 (actionDescription.asKnown().isPresent) 1 else 0) + + (actions.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (message.asKnown().isPresent) 1 else 0) + + (if (success.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Result && + actionDescription == other.actionDescription && + actions == other.actions && + message == other.message && + success == other.success && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(actionDescription, actions, message, success, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Result{actionDescription=$actionDescription, actions=$actions, message=$message, success=$success, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Data && + result == other.result && + actionId == other.actionId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(result, actionId, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Data{result=$result, actionId=$actionId, additionalProperties=$additionalProperties}" + } override fun equals(other: Any?): Boolean { if (this === other) { @@ -252,18 +697,15 @@ private constructor( } return other is SessionActResponse && - actions == other.actions && - message == other.message && + data == other.data && success == other.success && additionalProperties == other.additionalProperties } - private val hashCode: Int by lazy { - Objects.hash(actions, message, success, additionalProperties) - } + private val hashCode: Int by lazy { Objects.hash(data, success, additionalProperties) } override fun hashCode(): Int = hashCode override fun toString() = - "SessionActResponse{actions=$actions, message=$message, success=$success, additionalProperties=$additionalProperties}" + "SessionActResponse{data=$data, success=$success, additionalProperties=$additionalProperties}" } diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndParams.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndParams.kt index a3bb3fe..90a656f 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndParams.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndParams.kt @@ -2,25 +2,48 @@ package com.browserbase.api.models.sessions +import com.browserbase.api.core.Enum +import com.browserbase.api.core.JsonField import com.browserbase.api.core.JsonValue import com.browserbase.api.core.Params import com.browserbase.api.core.http.Headers import com.browserbase.api.core.http.QueryParams import com.browserbase.api.core.toImmutable +import com.browserbase.api.errors.StagehandInvalidDataException +import com.fasterxml.jackson.annotation.JsonCreator +import java.time.OffsetDateTime +import java.time.format.DateTimeFormatter import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** Closes the browser and cleans up all resources associated with the session. */ +/** Terminates the browser session and releases all associated resources. */ class SessionEndParams private constructor( - private val sessionId: String?, + private val id: String?, + private val xLanguage: XLanguage?, + private val xSdkVersion: String?, + private val xSentAt: OffsetDateTime?, + private val xStreamResponse: XStreamResponse?, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, private val additionalBodyProperties: Map, ) : Params { - fun sessionId(): Optional = Optional.ofNullable(sessionId) + /** Unique session identifier */ + fun id(): Optional = Optional.ofNullable(id) + + /** Client SDK language */ + fun xLanguage(): Optional = Optional.ofNullable(xLanguage) + + /** Version of the Stagehand SDK */ + fun xSdkVersion(): Optional = Optional.ofNullable(xSdkVersion) + + /** ISO timestamp when request was sent */ + fun xSentAt(): Optional = Optional.ofNullable(xSentAt) + + /** Whether to stream the response via SSE */ + fun xStreamResponse(): Optional = Optional.ofNullable(xStreamResponse) /** Additional body properties to send with the request. */ fun _additionalBodyProperties(): Map = additionalBodyProperties @@ -44,23 +67,59 @@ private constructor( /** A builder for [SessionEndParams]. */ class Builder internal constructor() { - private var sessionId: String? = null + private var id: String? = null + private var xLanguage: XLanguage? = null + private var xSdkVersion: String? = null + private var xSentAt: OffsetDateTime? = null + private var xStreamResponse: XStreamResponse? = null private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() private var additionalBodyProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(sessionEndParams: SessionEndParams) = apply { - sessionId = sessionEndParams.sessionId + id = sessionEndParams.id + xLanguage = sessionEndParams.xLanguage + xSdkVersion = sessionEndParams.xSdkVersion + xSentAt = sessionEndParams.xSentAt + xStreamResponse = sessionEndParams.xStreamResponse additionalHeaders = sessionEndParams.additionalHeaders.toBuilder() additionalQueryParams = sessionEndParams.additionalQueryParams.toBuilder() additionalBodyProperties = sessionEndParams.additionalBodyProperties.toMutableMap() } - fun sessionId(sessionId: String?) = apply { this.sessionId = sessionId } + /** Unique session identifier */ + fun id(id: String?) = apply { this.id = id } + + /** Alias for calling [Builder.id] with `id.orElse(null)`. */ + fun id(id: Optional) = id(id.getOrNull()) + + /** Client SDK language */ + fun xLanguage(xLanguage: XLanguage?) = apply { this.xLanguage = xLanguage } + + /** Alias for calling [Builder.xLanguage] with `xLanguage.orElse(null)`. */ + fun xLanguage(xLanguage: Optional) = xLanguage(xLanguage.getOrNull()) + + /** Version of the Stagehand SDK */ + fun xSdkVersion(xSdkVersion: String?) = apply { this.xSdkVersion = xSdkVersion } - /** Alias for calling [Builder.sessionId] with `sessionId.orElse(null)`. */ - fun sessionId(sessionId: Optional) = sessionId(sessionId.getOrNull()) + /** Alias for calling [Builder.xSdkVersion] with `xSdkVersion.orElse(null)`. */ + fun xSdkVersion(xSdkVersion: Optional) = xSdkVersion(xSdkVersion.getOrNull()) + + /** ISO timestamp when request was sent */ + fun xSentAt(xSentAt: OffsetDateTime?) = apply { this.xSentAt = xSentAt } + + /** Alias for calling [Builder.xSentAt] with `xSentAt.orElse(null)`. */ + fun xSentAt(xSentAt: Optional) = xSentAt(xSentAt.getOrNull()) + + /** Whether to stream the response via SSE */ + fun xStreamResponse(xStreamResponse: XStreamResponse?) = apply { + this.xStreamResponse = xStreamResponse + } + + /** Alias for calling [Builder.xStreamResponse] with `xStreamResponse.orElse(null)`. */ + fun xStreamResponse(xStreamResponse: Optional) = + xStreamResponse(xStreamResponse.getOrNull()) fun additionalHeaders(additionalHeaders: Headers) = apply { this.additionalHeaders.clear() @@ -189,7 +248,11 @@ private constructor( */ fun build(): SessionEndParams = SessionEndParams( - sessionId, + id, + xLanguage, + xSdkVersion, + xSentAt, + xStreamResponse, additionalHeaders.build(), additionalQueryParams.build(), additionalBodyProperties.toImmutable(), @@ -201,29 +264,319 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> sessionId ?: "" + 0 -> id ?: "" else -> "" } - override fun _headers(): Headers = additionalHeaders + override fun _headers(): Headers = + Headers.builder() + .apply { + xLanguage?.let { put("x-language", it.toString()) } + xSdkVersion?.let { put("x-sdk-version", it) } + xSentAt?.let { put("x-sent-at", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) } + xStreamResponse?.let { put("x-stream-response", it.toString()) } + putAll(additionalHeaders) + } + .build() override fun _queryParams(): QueryParams = additionalQueryParams + /** Client SDK language */ + class XLanguage @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 TYPESCRIPT = of("typescript") + + @JvmField val PYTHON = of("python") + + @JvmField val PLAYGROUND = of("playground") + + @JvmStatic fun of(value: String) = XLanguage(JsonField.of(value)) + } + + /** An enum containing [XLanguage]'s known values. */ + enum class Known { + TYPESCRIPT, + PYTHON, + PLAYGROUND, + } + + /** + * An enum containing [XLanguage]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [XLanguage] 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 { + TYPESCRIPT, + PYTHON, + PLAYGROUND, + /** + * An enum member indicating that [XLanguage] 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) { + TYPESCRIPT -> Value.TYPESCRIPT + PYTHON -> Value.PYTHON + PLAYGROUND -> Value.PLAYGROUND + 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) { + TYPESCRIPT -> Known.TYPESCRIPT + PYTHON -> Known.PYTHON + PLAYGROUND -> Known.PLAYGROUND + else -> throw StagehandInvalidDataException("Unknown XLanguage: $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 + + fun validate(): XLanguage = 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 XLanguage && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Whether to stream the response via SSE */ + class XStreamResponse @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 TRUE = of("true") + + @JvmField val FALSE = of("false") + + @JvmStatic fun of(value: String) = XStreamResponse(JsonField.of(value)) + } + + /** An enum containing [XStreamResponse]'s known values. */ + enum class Known { + TRUE, + FALSE, + } + + /** + * An enum containing [XStreamResponse]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [XStreamResponse] 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 { + TRUE, + FALSE, + /** + * An enum member indicating that [XStreamResponse] 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) { + TRUE -> Value.TRUE + FALSE -> Value.FALSE + 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) { + TRUE -> Known.TRUE + FALSE -> Known.FALSE + else -> throw StagehandInvalidDataException("Unknown XStreamResponse: $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 + + fun validate(): XStreamResponse = 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 XStreamResponse && 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 SessionEndParams && - sessionId == other.sessionId && + id == other.id && + xLanguage == other.xLanguage && + xSdkVersion == other.xSdkVersion && + xSentAt == other.xSentAt && + xStreamResponse == other.xStreamResponse && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams && additionalBodyProperties == other.additionalBodyProperties } override fun hashCode(): Int = - Objects.hash(sessionId, additionalHeaders, additionalQueryParams, additionalBodyProperties) + Objects.hash( + id, + xLanguage, + xSdkVersion, + xSentAt, + xStreamResponse, + additionalHeaders, + additionalQueryParams, + additionalBodyProperties, + ) override fun toString() = - "SessionEndParams{sessionId=$sessionId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" + "SessionEndParams{id=$id, xLanguage=$xLanguage, xSdkVersion=$xSdkVersion, xSentAt=$xSentAt, xStreamResponse=$xStreamResponse, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" } diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndResponse.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndResponse.kt index feba7d7..3a886e7 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndResponse.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndResponse.kt @@ -6,6 +6,7 @@ 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.checkRequired import com.browserbase.api.errors.StagehandInvalidDataException import com.fasterxml.jackson.annotation.JsonAnyGetter import com.fasterxml.jackson.annotation.JsonAnySetter @@ -13,7 +14,6 @@ import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty import java.util.Collections import java.util.Objects -import java.util.Optional class SessionEndResponse @JsonCreator(mode = JsonCreator.Mode.DISABLED) @@ -28,10 +28,12 @@ private constructor( ) : this(success, mutableMapOf()) /** - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected value). + * Indicates whether the request was successful + * + * @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 success(): Optional = success.getOptional("success") + fun success(): Boolean = success.getRequired("success") /** * Returns the raw JSON value of [success]. @@ -54,14 +56,21 @@ private constructor( companion object { - /** Returns a mutable builder for constructing an instance of [SessionEndResponse]. */ + /** + * Returns a mutable builder for constructing an instance of [SessionEndResponse]. + * + * The following fields are required: + * ```java + * .success() + * ``` + */ @JvmStatic fun builder() = Builder() } /** A builder for [SessionEndResponse]. */ class Builder internal constructor() { - private var success: JsonField = JsonMissing.of() + private var success: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic @@ -70,6 +79,7 @@ private constructor( additionalProperties = sessionEndResponse.additionalProperties.toMutableMap() } + /** Indicates whether the request was successful */ fun success(success: Boolean) = success(JsonField.of(success)) /** @@ -103,9 +113,19 @@ private constructor( * Returns an immutable instance of [SessionEndResponse]. * * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .success() + * ``` + * + * @throws IllegalStateException if any required field is unset. */ fun build(): SessionEndResponse = - SessionEndResponse(success, additionalProperties.toMutableMap()) + SessionEndResponse( + checkRequired("success", success), + additionalProperties.toMutableMap(), + ) } private var validated: Boolean = false diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteAgentResponse.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteAgentResponse.kt deleted file mode 100644 index 3c3af1d..0000000 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteAgentResponse.kt +++ /dev/null @@ -1,158 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. - -package com.browserbase.api.models.sessions - -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.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 java.util.Collections -import java.util.Objects -import java.util.Optional - -class SessionExecuteAgentResponse -@JsonCreator(mode = JsonCreator.Mode.DISABLED) -private constructor( - private val message: JsonField, - private val additionalProperties: MutableMap, -) { - - @JsonCreator - private constructor( - @JsonProperty("message") @ExcludeMissing message: JsonField = JsonMissing.of() - ) : this(message, mutableMapOf()) - - /** - * Final message from the agent - * - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected value). - */ - fun message(): Optional = message.getOptional("message") - - /** - * Returns the raw JSON value of [message]. - * - * Unlike [message], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("message") @ExcludeMissing fun _message(): JsonField = message - - @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 [SessionExecuteAgentResponse]. - */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [SessionExecuteAgentResponse]. */ - class Builder internal constructor() { - - private var message: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(sessionExecuteAgentResponse: SessionExecuteAgentResponse) = apply { - message = sessionExecuteAgentResponse.message - additionalProperties = sessionExecuteAgentResponse.additionalProperties.toMutableMap() - } - - /** Final message from the agent */ - fun message(message: String) = message(JsonField.of(message)) - - /** - * Sets [Builder.message] to an arbitrary JSON value. - * - * You should usually call [Builder.message] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun message(message: JsonField) = apply { this.message = message } - - 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 [SessionExecuteAgentResponse]. - * - * Further updates to this [Builder] will not mutate the returned instance. - */ - fun build(): SessionExecuteAgentResponse = - SessionExecuteAgentResponse(message, additionalProperties.toMutableMap()) - } - - private var validated: Boolean = false - - fun validate(): SessionExecuteAgentResponse = apply { - if (validated) { - return@apply - } - - message() - 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 (message.asKnown().isPresent) 1 else 0) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return other is SessionExecuteAgentResponse && - message == other.message && - additionalProperties == other.additionalProperties - } - - private val hashCode: Int by lazy { Objects.hash(message, additionalProperties) } - - override fun hashCode(): Int = hashCode - - override fun toString() = - "SessionExecuteAgentResponse{message=$message, additionalProperties=$additionalProperties}" -} diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteAgentParams.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteParams.kt similarity index 71% rename from stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteAgentParams.kt rename to stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteParams.kt index d2c5c47..73880ff 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteAgentParams.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteParams.kt @@ -2,17 +2,13 @@ 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.Params -import com.browserbase.api.core.allMaxBy 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 import com.browserbase.api.errors.StagehandInvalidDataException @@ -20,30 +16,39 @@ 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.time.OffsetDateTime +import java.time.format.DateTimeFormatter import java.util.Collections import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** Runs an autonomous agent that can perform multiple actions to complete a complex task. */ -class SessionExecuteAgentParams +/** Runs an autonomous AI agent that can perform complex multi-step browser tasks. */ +class SessionExecuteParams private constructor( - private val sessionId: String?, + private val id: String?, + private val xLanguage: XLanguage?, + private val xSdkVersion: String?, + private val xSentAt: OffsetDateTime?, private val xStreamResponse: XStreamResponse?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun sessionId(): Optional = Optional.ofNullable(sessionId) + /** Unique session identifier */ + fun id(): Optional = Optional.ofNullable(id) + /** Client SDK language */ + fun xLanguage(): Optional = Optional.ofNullable(xLanguage) + + /** Version of the Stagehand SDK */ + fun xSdkVersion(): Optional = Optional.ofNullable(xSdkVersion) + + /** ISO timestamp when request was sent */ + fun xSentAt(): Optional = Optional.ofNullable(xSentAt) + + /** Whether to stream the response via SSE */ fun xStreamResponse(): Optional = Optional.ofNullable(xStreamResponse) /** @@ -59,6 +64,8 @@ private constructor( fun executeOptions(): ExecuteOptions = body.executeOptions() /** + * Target frame ID for the agent + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ @@ -98,7 +105,7 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [SessionExecuteAgentParams]. + * Returns a mutable builder for constructing an instance of [SessionExecuteParams]. * * The following fields are required: * ```java @@ -109,29 +116,55 @@ private constructor( @JvmStatic fun builder() = Builder() } - /** A builder for [SessionExecuteAgentParams]. */ + /** A builder for [SessionExecuteParams]. */ class Builder internal constructor() { - private var sessionId: String? = null + private var id: String? = null + private var xLanguage: XLanguage? = null + private var xSdkVersion: String? = null + private var xSentAt: OffsetDateTime? = null private var xStreamResponse: XStreamResponse? = null private var body: Body.Builder = Body.builder() private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @JvmSynthetic - internal fun from(sessionExecuteAgentParams: SessionExecuteAgentParams) = apply { - sessionId = sessionExecuteAgentParams.sessionId - xStreamResponse = sessionExecuteAgentParams.xStreamResponse - body = sessionExecuteAgentParams.body.toBuilder() - additionalHeaders = sessionExecuteAgentParams.additionalHeaders.toBuilder() - additionalQueryParams = sessionExecuteAgentParams.additionalQueryParams.toBuilder() + internal fun from(sessionExecuteParams: SessionExecuteParams) = apply { + id = sessionExecuteParams.id + xLanguage = sessionExecuteParams.xLanguage + xSdkVersion = sessionExecuteParams.xSdkVersion + xSentAt = sessionExecuteParams.xSentAt + xStreamResponse = sessionExecuteParams.xStreamResponse + body = sessionExecuteParams.body.toBuilder() + additionalHeaders = sessionExecuteParams.additionalHeaders.toBuilder() + additionalQueryParams = sessionExecuteParams.additionalQueryParams.toBuilder() } - fun sessionId(sessionId: String?) = apply { this.sessionId = sessionId } + /** Unique session identifier */ + fun id(id: String?) = apply { this.id = id } + + /** Alias for calling [Builder.id] with `id.orElse(null)`. */ + fun id(id: Optional) = id(id.getOrNull()) + + /** Client SDK language */ + fun xLanguage(xLanguage: XLanguage?) = apply { this.xLanguage = xLanguage } + + /** Alias for calling [Builder.xLanguage] with `xLanguage.orElse(null)`. */ + fun xLanguage(xLanguage: Optional) = xLanguage(xLanguage.getOrNull()) + + /** Version of the Stagehand SDK */ + fun xSdkVersion(xSdkVersion: String?) = apply { this.xSdkVersion = xSdkVersion } - /** Alias for calling [Builder.sessionId] with `sessionId.orElse(null)`. */ - fun sessionId(sessionId: Optional) = sessionId(sessionId.getOrNull()) + /** Alias for calling [Builder.xSdkVersion] with `xSdkVersion.orElse(null)`. */ + fun xSdkVersion(xSdkVersion: Optional) = xSdkVersion(xSdkVersion.getOrNull()) + /** ISO timestamp when request was sent */ + fun xSentAt(xSentAt: OffsetDateTime?) = apply { this.xSentAt = xSentAt } + + /** Alias for calling [Builder.xSentAt] with `xSentAt.orElse(null)`. */ + fun xSentAt(xSentAt: Optional) = xSentAt(xSentAt.getOrNull()) + + /** Whether to stream the response via SSE */ fun xStreamResponse(xStreamResponse: XStreamResponse?) = apply { this.xStreamResponse = xStreamResponse } @@ -179,6 +212,7 @@ private constructor( body.executeOptions(executeOptions) } + /** Target frame ID for the agent */ fun frameId(frameId: String) = apply { body.frameId(frameId) } /** @@ -307,7 +341,7 @@ private constructor( } /** - * Returns an immutable instance of [SessionExecuteAgentParams]. + * Returns an immutable instance of [SessionExecuteParams]. * * Further updates to this [Builder] will not mutate the returned instance. * @@ -319,9 +353,12 @@ private constructor( * * @throws IllegalStateException if any required field is unset. */ - fun build(): SessionExecuteAgentParams = - SessionExecuteAgentParams( - sessionId, + fun build(): SessionExecuteParams = + SessionExecuteParams( + id, + xLanguage, + xSdkVersion, + xSentAt, xStreamResponse, body.build(), additionalHeaders.build(), @@ -333,13 +370,16 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> sessionId ?: "" + 0 -> id ?: "" else -> "" } override fun _headers(): Headers = Headers.builder() .apply { + xLanguage?.let { put("x-language", it.toString()) } + xSdkVersion?.let { put("x-sdk-version", it) } + xSentAt?.let { put("x-sent-at", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) } xStreamResponse?.let { put("x-stream-response", it.toString()) } putAll(additionalHeaders) } @@ -380,6 +420,8 @@ private constructor( fun executeOptions(): ExecuteOptions = executeOptions.getRequired("executeOptions") /** + * Target frame ID for the agent + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ @@ -480,6 +522,7 @@ private constructor( this.executeOptions = executeOptions } + /** Target frame ID for the agent */ fun frameId(frameId: String) = frameId(JsonField.of(frameId)) /** @@ -591,8 +634,7 @@ private constructor( @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val cua: JsonField, - private val model: JsonField, - private val provider: JsonField, + private val model: JsonField, private val systemPrompt: JsonField, private val additionalProperties: MutableMap, ) { @@ -600,14 +642,11 @@ private constructor( @JsonCreator private constructor( @JsonProperty("cua") @ExcludeMissing cua: JsonField = JsonMissing.of(), - @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), - @JsonProperty("provider") - @ExcludeMissing - provider: JsonField = JsonMissing.of(), + @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), @JsonProperty("systemPrompt") @ExcludeMissing systemPrompt: JsonField = JsonMissing.of(), - ) : this(cua, model, provider, systemPrompt, mutableMapOf()) + ) : this(cua, model, systemPrompt, mutableMapOf()) /** * Enable Computer Use Agent mode @@ -618,18 +657,17 @@ private constructor( fun cua(): Optional = cua.getOptional("cua") /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun model(): Optional = model.getOptional("model") - - /** - * @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") + fun model(): Optional = model.getOptional("model") /** + * Custom system prompt for the agent + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ @@ -647,14 +685,7 @@ private constructor( * * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model - - /** - * Returns the raw JSON value of [provider]. - * - * Unlike [provider], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonField = provider + @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model /** * Returns the raw JSON value of [systemPrompt]. @@ -688,8 +719,7 @@ private constructor( class Builder internal constructor() { private var cua: JsonField = JsonMissing.of() - private var model: JsonField = JsonMissing.of() - private var provider: JsonField = JsonMissing.of() + private var model: JsonField = JsonMissing.of() private var systemPrompt: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -697,7 +727,6 @@ private constructor( internal fun from(agentConfig: AgentConfig) = apply { cua = agentConfig.cua model = agentConfig.model - provider = agentConfig.provider systemPrompt = agentConfig.systemPrompt additionalProperties = agentConfig.additionalProperties.toMutableMap() } @@ -714,34 +743,31 @@ private constructor( */ fun cua(cua: JsonField) = apply { this.cua = cua } - fun model(model: Model) = model(JsonField.of(model)) + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') + */ + fun model(model: ModelConfig) = model(JsonField.of(model)) /** * Sets [Builder.model] to an arbitrary JSON value. * - * You should usually call [Builder.model] with a well-typed [Model] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported - * value. + * You should usually call [Builder.model] with a well-typed [ModelConfig] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. */ - fun model(model: JsonField) = apply { this.model = model } - - /** Alias for calling [model] with `Model.ofString(string)`. */ - fun model(string: String) = model(Model.ofString(string)) + fun model(model: JsonField) = apply { this.model = model } - /** Alias for calling [model] with `Model.ofConfig(config)`. */ - fun model(config: ModelConfig) = model(Model.ofConfig(config)) - - fun provider(provider: Provider) = provider(JsonField.of(provider)) + /** Alias for calling [model] with `ModelConfig.ofName(name)`. */ + fun model(name: String) = model(ModelConfig.ofName(name)) /** - * 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. + * Alias for calling [model] with `ModelConfig.ofModelConfigObject(modelConfigObject)`. */ - fun provider(provider: JsonField) = apply { this.provider = provider } + fun model(modelConfigObject: ModelConfig.ModelConfigObject) = + model(ModelConfig.ofModelConfigObject(modelConfigObject)) + /** Custom system prompt for the agent */ fun systemPrompt(systemPrompt: String) = systemPrompt(JsonField.of(systemPrompt)) /** @@ -780,7 +806,7 @@ private constructor( * Further updates to this [Builder] will not mutate the returned instance. */ fun build(): AgentConfig = - AgentConfig(cua, model, provider, systemPrompt, additionalProperties.toMutableMap()) + AgentConfig(cua, model, systemPrompt, additionalProperties.toMutableMap()) } private var validated: Boolean = false @@ -792,7 +818,6 @@ private constructor( cua() model().ifPresent { it.validate() } - provider().ifPresent { it.validate() } systemPrompt() validated = true } @@ -815,316 +840,8 @@ private constructor( internal fun validity(): Int = (if (cua.asKnown().isPresent) 1 else 0) + (model.asKnown().getOrNull()?.validity() ?: 0) + - (provider.asKnown().getOrNull()?.validity() ?: 0) + (if (systemPrompt.asKnown().isPresent) 1 else 0) - @JsonDeserialize(using = Model.Deserializer::class) - @JsonSerialize(using = Model.Serializer::class) - class Model - private constructor( - private val string: String? = null, - private val config: ModelConfig? = null, - private val _json: JsonValue? = null, - ) { - - fun string(): Optional = Optional.ofNullable(string) - - fun config(): Optional = Optional.ofNullable(config) - - fun isString(): Boolean = string != null - - fun isConfig(): Boolean = config != null - - fun asString(): String = string.getOrThrow("string") - - fun asConfig(): ModelConfig = config.getOrThrow("config") - - fun _json(): Optional = Optional.ofNullable(_json) - - fun accept(visitor: Visitor): T = - when { - string != null -> visitor.visitString(string) - config != null -> visitor.visitConfig(config) - else -> visitor.unknown(_json) - } - - private var validated: Boolean = false - - fun validate(): Model = apply { - if (validated) { - return@apply - } - - accept( - object : Visitor { - override fun visitString(string: String) {} - - override fun visitConfig(config: ModelConfig) { - config.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 = - accept( - object : Visitor { - override fun visitString(string: String) = 1 - - override fun visitConfig(config: ModelConfig) = config.validity() - - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return other is Model && string == other.string && config == other.config - } - - override fun hashCode(): Int = Objects.hash(string, config) - - override fun toString(): String = - when { - string != null -> "Model{string=$string}" - config != null -> "Model{config=$config}" - _json != null -> "Model{_unknown=$_json}" - else -> throw IllegalStateException("Invalid Model") - } - - companion object { - - @JvmStatic fun ofString(string: String) = Model(string = string) - - @JvmStatic fun ofConfig(config: ModelConfig) = Model(config = config) - } - - /** - * An interface that defines how to map each variant of [Model] to a value of type [T]. - */ - interface Visitor { - - fun visitString(string: String): T - - fun visitConfig(config: ModelConfig): 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(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 array). - 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.string != null -> generator.writeObject(value.string) - value.config != null -> generator.writeObject(value.config) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid Model") - } - } - } - } - - 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") - - @JvmStatic fun of(value: String) = Provider(JsonField.of(value)) - } - - /** An enum containing [Provider]'s known values. */ - enum class Known { - OPENAI, - ANTHROPIC, - GOOGLE, - } - - /** - * 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, - /** - * 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 - 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 - 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 - - 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 @@ -1133,19 +850,18 @@ private constructor( return other is AgentConfig && cua == other.cua && model == other.model && - provider == other.provider && systemPrompt == other.systemPrompt && additionalProperties == other.additionalProperties } private val hashCode: Int by lazy { - Objects.hash(cua, model, provider, systemPrompt, additionalProperties) + Objects.hash(cua, model, systemPrompt, additionalProperties) } override fun hashCode(): Int = hashCode override fun toString() = - "AgentConfig{cua=$cua, model=$model, provider=$provider, systemPrompt=$systemPrompt, additionalProperties=$additionalProperties}" + "AgentConfig{cua=$cua, model=$model, systemPrompt=$systemPrompt, additionalProperties=$additionalProperties}" } class ExecuteOptions @@ -1153,7 +869,7 @@ private constructor( private constructor( private val instruction: JsonField, private val highlightCursor: JsonField, - private val maxSteps: JsonField, + private val maxSteps: JsonField, private val additionalProperties: MutableMap, ) { @@ -1165,11 +881,11 @@ private constructor( @JsonProperty("highlightCursor") @ExcludeMissing highlightCursor: JsonField = JsonMissing.of(), - @JsonProperty("maxSteps") @ExcludeMissing maxSteps: JsonField = JsonMissing.of(), + @JsonProperty("maxSteps") @ExcludeMissing maxSteps: JsonField = JsonMissing.of(), ) : this(instruction, highlightCursor, maxSteps, mutableMapOf()) /** - * Task for the agent to complete + * Natural language instruction for the agent * * @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). @@ -1177,7 +893,7 @@ private constructor( fun instruction(): String = instruction.getRequired("instruction") /** - * Visually highlight the cursor during actions + * Whether to visually highlight the cursor during execution * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). @@ -1190,7 +906,7 @@ private constructor( * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun maxSteps(): Optional = maxSteps.getOptional("maxSteps") + fun maxSteps(): Optional = maxSteps.getOptional("maxSteps") /** * Returns the raw JSON value of [instruction]. @@ -1216,7 +932,7 @@ private constructor( * * Unlike [maxSteps], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("maxSteps") @ExcludeMissing fun _maxSteps(): JsonField = maxSteps + @JsonProperty("maxSteps") @ExcludeMissing fun _maxSteps(): JsonField = maxSteps @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -1248,7 +964,7 @@ private constructor( private var instruction: JsonField? = null private var highlightCursor: JsonField = JsonMissing.of() - private var maxSteps: JsonField = JsonMissing.of() + private var maxSteps: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic @@ -1259,7 +975,7 @@ private constructor( additionalProperties = executeOptions.additionalProperties.toMutableMap() } - /** Task for the agent to complete */ + /** Natural language instruction for the agent */ fun instruction(instruction: String) = instruction(JsonField.of(instruction)) /** @@ -1273,7 +989,7 @@ private constructor( this.instruction = instruction } - /** Visually highlight the cursor during actions */ + /** Whether to visually highlight the cursor during execution */ fun highlightCursor(highlightCursor: Boolean) = highlightCursor(JsonField.of(highlightCursor)) @@ -1289,16 +1005,16 @@ private constructor( } /** Maximum number of steps the agent can take */ - fun maxSteps(maxSteps: Long) = maxSteps(JsonField.of(maxSteps)) + fun maxSteps(maxSteps: Double) = maxSteps(JsonField.of(maxSteps)) /** * Sets [Builder.maxSteps] to an arbitrary JSON value. * - * You should usually call [Builder.maxSteps] with a well-typed [Long] value instead. + * You should usually call [Builder.maxSteps] with a well-typed [Double] value instead. * This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun maxSteps(maxSteps: JsonField) = apply { this.maxSteps = maxSteps } + fun maxSteps(maxSteps: JsonField) = apply { this.maxSteps = maxSteps } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -1395,6 +1111,143 @@ private constructor( "ExecuteOptions{instruction=$instruction, highlightCursor=$highlightCursor, maxSteps=$maxSteps, additionalProperties=$additionalProperties}" } + /** Client SDK language */ + class XLanguage @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 TYPESCRIPT = of("typescript") + + @JvmField val PYTHON = of("python") + + @JvmField val PLAYGROUND = of("playground") + + @JvmStatic fun of(value: String) = XLanguage(JsonField.of(value)) + } + + /** An enum containing [XLanguage]'s known values. */ + enum class Known { + TYPESCRIPT, + PYTHON, + PLAYGROUND, + } + + /** + * An enum containing [XLanguage]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [XLanguage] 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 { + TYPESCRIPT, + PYTHON, + PLAYGROUND, + /** + * An enum member indicating that [XLanguage] 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) { + TYPESCRIPT -> Value.TYPESCRIPT + PYTHON -> Value.PYTHON + PLAYGROUND -> Value.PLAYGROUND + 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) { + TYPESCRIPT -> Known.TYPESCRIPT + PYTHON -> Known.PYTHON + PLAYGROUND -> Known.PLAYGROUND + else -> throw StagehandInvalidDataException("Unknown XLanguage: $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 + + fun validate(): XLanguage = 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 XLanguage && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Whether to stream the response via SSE */ class XStreamResponse @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -1531,8 +1384,11 @@ private constructor( return true } - return other is SessionExecuteAgentParams && - sessionId == other.sessionId && + return other is SessionExecuteParams && + id == other.id && + xLanguage == other.xLanguage && + xSdkVersion == other.xSdkVersion && + xSentAt == other.xSentAt && xStreamResponse == other.xStreamResponse && body == other.body && additionalHeaders == other.additionalHeaders && @@ -1540,8 +1396,17 @@ private constructor( } override fun hashCode(): Int = - Objects.hash(sessionId, xStreamResponse, body, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + xLanguage, + xSdkVersion, + xSentAt, + xStreamResponse, + body, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "SessionExecuteAgentParams{sessionId=$sessionId, xStreamResponse=$xStreamResponse, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "SessionExecuteParams{id=$id, xLanguage=$xLanguage, xSdkVersion=$xSdkVersion, xSentAt=$xSentAt, xStreamResponse=$xStreamResponse, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteResponse.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteResponse.kt new file mode 100644 index 0000000..8718994 --- /dev/null +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteResponse.kt @@ -0,0 +1,1658 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.browserbase.api.models.sessions + +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.checkKnown +import com.browserbase.api.core.checkRequired +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 java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class SessionExecuteResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val data: JsonField, + private val success: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(), + @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(), + ) : this(data, success, 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 data(): Data = data.getRequired("data") + + /** + * Indicates whether the request was successful + * + * @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 success(): Boolean = success.getRequired("success") + + /** + * Returns the raw JSON value of [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField = data + + /** + * Returns the raw JSON value of [success]. + * + * Unlike [success], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success + + @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 [SessionExecuteResponse]. + * + * The following fields are required: + * ```java + * .data() + * .success() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [SessionExecuteResponse]. */ + class Builder internal constructor() { + + private var data: JsonField? = null + private var success: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(sessionExecuteResponse: SessionExecuteResponse) = apply { + data = sessionExecuteResponse.data + success = sessionExecuteResponse.success + additionalProperties = sessionExecuteResponse.additionalProperties.toMutableMap() + } + + fun data(data: Data) = data(JsonField.of(data)) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed [Data] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun data(data: JsonField) = apply { this.data = data } + + /** Indicates whether the request was successful */ + fun success(success: Boolean) = success(JsonField.of(success)) + + /** + * Sets [Builder.success] to an arbitrary JSON value. + * + * You should usually call [Builder.success] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun success(success: JsonField) = apply { this.success = success } + + 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 [SessionExecuteResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .data() + * .success() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): SessionExecuteResponse = + SessionExecuteResponse( + checkRequired("data", data), + checkRequired("success", success), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): SessionExecuteResponse = apply { + if (validated) { + return@apply + } + + data().validate() + success() + 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + (if (success.asKnown().isPresent) 1 else 0) + + class Data + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val result: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("result") @ExcludeMissing result: JsonField = JsonMissing.of() + ) : this(result, 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 result(): Result = result.getRequired("result") + + /** + * Returns the raw JSON value of [result]. + * + * Unlike [result], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("result") @ExcludeMissing fun _result(): JsonField = result + + @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 [Data]. + * + * The following fields are required: + * ```java + * .result() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Data]. */ + class Builder internal constructor() { + + private var result: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(data: Data) = apply { + result = data.result + additionalProperties = data.additionalProperties.toMutableMap() + } + + fun result(result: Result) = result(JsonField.of(result)) + + /** + * Sets [Builder.result] to an arbitrary JSON value. + * + * You should usually call [Builder.result] with a well-typed [Result] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun result(result: JsonField) = apply { this.result = result } + + 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 [Data]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .result() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Data = + Data(checkRequired("result", result), additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Data = apply { + if (validated) { + return@apply + } + + result().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 = (result.asKnown().getOrNull()?.validity() ?: 0) + + class Result + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val actions: JsonField>, + private val completed: JsonField, + private val message: JsonField, + private val success: JsonField, + private val metadata: JsonField, + private val usage: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("actions") + @ExcludeMissing + actions: JsonField> = JsonMissing.of(), + @JsonProperty("completed") + @ExcludeMissing + completed: JsonField = JsonMissing.of(), + @JsonProperty("message") + @ExcludeMissing + message: JsonField = JsonMissing.of(), + @JsonProperty("success") + @ExcludeMissing + success: JsonField = JsonMissing.of(), + @JsonProperty("metadata") + @ExcludeMissing + metadata: JsonField = JsonMissing.of(), + @JsonProperty("usage") @ExcludeMissing usage: JsonField = JsonMissing.of(), + ) : this(actions, completed, message, success, metadata, usage, 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 actions(): List = actions.getRequired("actions") + + /** + * Whether the agent finished its task + * + * @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 completed(): Boolean = completed.getRequired("completed") + + /** + * Summary of what the agent accomplished + * + * @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 message(): String = message.getRequired("message") + + /** + * Whether the agent completed successfully + * + * @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 success(): Boolean = success.getRequired("success") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun metadata(): Optional = metadata.getOptional("metadata") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun usage(): Optional = usage.getOptional("usage") + + /** + * Returns the raw JSON value of [actions]. + * + * Unlike [actions], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("actions") + @ExcludeMissing + fun _actions(): JsonField> = actions + + /** + * Returns the raw JSON value of [completed]. + * + * Unlike [completed], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("completed") + @ExcludeMissing + fun _completed(): JsonField = completed + + /** + * Returns the raw JSON value of [message]. + * + * Unlike [message], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("message") @ExcludeMissing fun _message(): JsonField = message + + /** + * Returns the raw JSON value of [success]. + * + * Unlike [success], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success + + /** + * Returns the raw JSON value of [metadata]. + * + * Unlike [metadata], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("metadata") + @ExcludeMissing + fun _metadata(): JsonField = metadata + + /** + * Returns the raw JSON value of [usage]. + * + * Unlike [usage], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("usage") @ExcludeMissing fun _usage(): JsonField = usage + + @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 [Result]. + * + * The following fields are required: + * ```java + * .actions() + * .completed() + * .message() + * .success() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Result]. */ + class Builder internal constructor() { + + private var actions: JsonField>? = null + private var completed: JsonField? = null + private var message: JsonField? = null + private var success: JsonField? = null + private var metadata: JsonField = JsonMissing.of() + private var usage: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(result: Result) = apply { + actions = result.actions.map { it.toMutableList() } + completed = result.completed + message = result.message + success = result.success + metadata = result.metadata + usage = result.usage + additionalProperties = result.additionalProperties.toMutableMap() + } + + fun actions(actions: List) = actions(JsonField.of(actions)) + + /** + * Sets [Builder.actions] to an arbitrary JSON value. + * + * You should usually call [Builder.actions] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun actions(actions: JsonField>) = apply { + this.actions = actions.map { it.toMutableList() } + } + + /** + * Adds a single [Action] to [actions]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addAction(action: Action) = apply { + actions = + (actions ?: JsonField.of(mutableListOf())).also { + checkKnown("actions", it).add(action) + } + } + + /** Whether the agent finished its task */ + fun completed(completed: Boolean) = completed(JsonField.of(completed)) + + /** + * Sets [Builder.completed] to an arbitrary JSON value. + * + * You should usually call [Builder.completed] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun completed(completed: JsonField) = apply { this.completed = completed } + + /** Summary of what the agent accomplished */ + fun message(message: String) = message(JsonField.of(message)) + + /** + * Sets [Builder.message] to an arbitrary JSON value. + * + * You should usually call [Builder.message] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun message(message: JsonField) = apply { this.message = message } + + /** Whether the agent completed successfully */ + fun success(success: Boolean) = success(JsonField.of(success)) + + /** + * Sets [Builder.success] to an arbitrary JSON value. + * + * You should usually call [Builder.success] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun success(success: JsonField) = apply { this.success = success } + + fun metadata(metadata: Metadata) = metadata(JsonField.of(metadata)) + + /** + * Sets [Builder.metadata] to an arbitrary JSON value. + * + * You should usually call [Builder.metadata] with a well-typed [Metadata] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun metadata(metadata: JsonField) = apply { this.metadata = metadata } + + fun usage(usage: Usage) = usage(JsonField.of(usage)) + + /** + * Sets [Builder.usage] to an arbitrary JSON value. + * + * You should usually call [Builder.usage] with a well-typed [Usage] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun usage(usage: JsonField) = apply { this.usage = usage } + + 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 [Result]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .actions() + * .completed() + * .message() + * .success() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Result = + Result( + checkRequired("actions", actions).map { it.toImmutable() }, + checkRequired("completed", completed), + checkRequired("message", message), + checkRequired("success", success), + metadata, + usage, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Result = apply { + if (validated) { + return@apply + } + + actions().forEach { it.validate() } + completed() + message() + success() + metadata().ifPresent { it.validate() } + usage().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 = + (actions.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (completed.asKnown().isPresent) 1 else 0) + + (if (message.asKnown().isPresent) 1 else 0) + + (if (success.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (usage.asKnown().getOrNull()?.validity() ?: 0) + + class Action + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val type: JsonField, + private val action: JsonField, + private val instruction: JsonField, + private val pageText: JsonField, + private val pageUrl: JsonField, + private val reasoning: JsonField, + private val taskCompleted: JsonField, + private val timeMs: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("type") + @ExcludeMissing + type: JsonField = JsonMissing.of(), + @JsonProperty("action") + @ExcludeMissing + action: JsonField = JsonMissing.of(), + @JsonProperty("instruction") + @ExcludeMissing + instruction: JsonField = JsonMissing.of(), + @JsonProperty("pageText") + @ExcludeMissing + pageText: JsonField = JsonMissing.of(), + @JsonProperty("pageUrl") + @ExcludeMissing + pageUrl: JsonField = JsonMissing.of(), + @JsonProperty("reasoning") + @ExcludeMissing + reasoning: JsonField = JsonMissing.of(), + @JsonProperty("taskCompleted") + @ExcludeMissing + taskCompleted: JsonField = JsonMissing.of(), + @JsonProperty("timeMs") + @ExcludeMissing + timeMs: JsonField = JsonMissing.of(), + ) : this( + type, + action, + instruction, + pageText, + pageUrl, + reasoning, + taskCompleted, + timeMs, + mutableMapOf(), + ) + + /** + * Type of action taken + * + * @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 type(): String = type.getRequired("type") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun action(): Optional = action.getOptional("action") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun instruction(): Optional = instruction.getOptional("instruction") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun pageText(): Optional = pageText.getOptional("pageText") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun pageUrl(): Optional = pageUrl.getOptional("pageUrl") + + /** + * Agent's reasoning for taking this action + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun reasoning(): Optional = reasoning.getOptional("reasoning") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun taskCompleted(): Optional = taskCompleted.getOptional("taskCompleted") + + /** + * Time taken for this action in ms + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun timeMs(): Optional = timeMs.getOptional("timeMs") + + /** + * 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 [action]. + * + * Unlike [action], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("action") @ExcludeMissing fun _action(): JsonField = action + + /** + * Returns the raw JSON value of [instruction]. + * + * Unlike [instruction], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("instruction") + @ExcludeMissing + fun _instruction(): JsonField = instruction + + /** + * Returns the raw JSON value of [pageText]. + * + * Unlike [pageText], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pageText") + @ExcludeMissing + fun _pageText(): JsonField = pageText + + /** + * Returns the raw JSON value of [pageUrl]. + * + * Unlike [pageUrl], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pageUrl") @ExcludeMissing fun _pageUrl(): JsonField = pageUrl + + /** + * Returns the raw JSON value of [reasoning]. + * + * Unlike [reasoning], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("reasoning") + @ExcludeMissing + fun _reasoning(): JsonField = reasoning + + /** + * Returns the raw JSON value of [taskCompleted]. + * + * Unlike [taskCompleted], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("taskCompleted") + @ExcludeMissing + fun _taskCompleted(): JsonField = taskCompleted + + /** + * Returns the raw JSON value of [timeMs]. + * + * Unlike [timeMs], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("timeMs") @ExcludeMissing fun _timeMs(): JsonField = timeMs + + @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 [Action]. + * + * The following fields are required: + * ```java + * .type() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Action]. */ + class Builder internal constructor() { + + private var type: JsonField? = null + private var action: JsonField = JsonMissing.of() + private var instruction: JsonField = JsonMissing.of() + private var pageText: JsonField = JsonMissing.of() + private var pageUrl: JsonField = JsonMissing.of() + private var reasoning: JsonField = JsonMissing.of() + private var taskCompleted: JsonField = JsonMissing.of() + private var timeMs: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(action: Action) = apply { + type = action.type + this.action = action.action + instruction = action.instruction + pageText = action.pageText + pageUrl = action.pageUrl + reasoning = action.reasoning + taskCompleted = action.taskCompleted + timeMs = action.timeMs + additionalProperties = action.additionalProperties.toMutableMap() + } + + /** Type of action taken */ + fun type(type: String) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun type(type: JsonField) = apply { this.type = type } + + fun action(action: String) = action(JsonField.of(action)) + + /** + * Sets [Builder.action] to an arbitrary JSON value. + * + * You should usually call [Builder.action] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun action(action: JsonField) = apply { this.action = action } + + fun instruction(instruction: String) = instruction(JsonField.of(instruction)) + + /** + * Sets [Builder.instruction] to an arbitrary JSON value. + * + * You should usually call [Builder.instruction] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun instruction(instruction: JsonField) = apply { + this.instruction = instruction + } + + fun pageText(pageText: String) = pageText(JsonField.of(pageText)) + + /** + * Sets [Builder.pageText] to an arbitrary JSON value. + * + * You should usually call [Builder.pageText] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun pageText(pageText: JsonField) = apply { this.pageText = pageText } + + fun pageUrl(pageUrl: String) = pageUrl(JsonField.of(pageUrl)) + + /** + * Sets [Builder.pageUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.pageUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun pageUrl(pageUrl: JsonField) = apply { this.pageUrl = pageUrl } + + /** Agent's reasoning for taking this action */ + fun reasoning(reasoning: String) = reasoning(JsonField.of(reasoning)) + + /** + * Sets [Builder.reasoning] to an arbitrary JSON value. + * + * You should usually call [Builder.reasoning] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun reasoning(reasoning: JsonField) = apply { + this.reasoning = reasoning + } + + fun taskCompleted(taskCompleted: Boolean) = + taskCompleted(JsonField.of(taskCompleted)) + + /** + * Sets [Builder.taskCompleted] to an arbitrary JSON value. + * + * You should usually call [Builder.taskCompleted] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun taskCompleted(taskCompleted: JsonField) = apply { + this.taskCompleted = taskCompleted + } + + /** Time taken for this action in ms */ + fun timeMs(timeMs: Double) = timeMs(JsonField.of(timeMs)) + + /** + * Sets [Builder.timeMs] to an arbitrary JSON value. + * + * You should usually call [Builder.timeMs] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun timeMs(timeMs: JsonField) = apply { this.timeMs = timeMs } + + 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 [Action]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .type() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Action = + Action( + checkRequired("type", type), + action, + instruction, + pageText, + pageUrl, + reasoning, + taskCompleted, + timeMs, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Action = apply { + if (validated) { + return@apply + } + + type() + action() + instruction() + pageText() + pageUrl() + reasoning() + taskCompleted() + timeMs() + 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 (type.asKnown().isPresent) 1 else 0) + + (if (action.asKnown().isPresent) 1 else 0) + + (if (instruction.asKnown().isPresent) 1 else 0) + + (if (pageText.asKnown().isPresent) 1 else 0) + + (if (pageUrl.asKnown().isPresent) 1 else 0) + + (if (reasoning.asKnown().isPresent) 1 else 0) + + (if (taskCompleted.asKnown().isPresent) 1 else 0) + + (if (timeMs.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Action && + type == other.type && + action == other.action && + instruction == other.instruction && + pageText == other.pageText && + pageUrl == other.pageUrl && + reasoning == other.reasoning && + taskCompleted == other.taskCompleted && + timeMs == other.timeMs && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + type, + action, + instruction, + pageText, + pageUrl, + reasoning, + taskCompleted, + timeMs, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Action{type=$type, action=$action, instruction=$instruction, pageText=$pageText, pageUrl=$pageUrl, reasoning=$reasoning, taskCompleted=$taskCompleted, timeMs=$timeMs, additionalProperties=$additionalProperties}" + } + + class Metadata + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Metadata]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Metadata]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(metadata: Metadata) = apply { + additionalProperties = metadata.additionalProperties.toMutableMap() + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Metadata]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Metadata = Metadata(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): Metadata = 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 Metadata && additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Metadata{additionalProperties=$additionalProperties}" + } + + class Usage + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val inferenceTimeMs: JsonField, + private val inputTokens: JsonField, + private val outputTokens: JsonField, + private val cachedInputTokens: JsonField, + private val reasoningTokens: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("inference_time_ms") + @ExcludeMissing + inferenceTimeMs: JsonField = JsonMissing.of(), + @JsonProperty("input_tokens") + @ExcludeMissing + inputTokens: JsonField = JsonMissing.of(), + @JsonProperty("output_tokens") + @ExcludeMissing + outputTokens: JsonField = JsonMissing.of(), + @JsonProperty("cached_input_tokens") + @ExcludeMissing + cachedInputTokens: JsonField = JsonMissing.of(), + @JsonProperty("reasoning_tokens") + @ExcludeMissing + reasoningTokens: JsonField = JsonMissing.of(), + ) : this( + inferenceTimeMs, + inputTokens, + outputTokens, + cachedInputTokens, + reasoningTokens, + 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 inferenceTimeMs(): Double = inferenceTimeMs.getRequired("inference_time_ms") + + /** + * @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 inputTokens(): Double = inputTokens.getRequired("input_tokens") + + /** + * @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 outputTokens(): Double = outputTokens.getRequired("output_tokens") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun cachedInputTokens(): Optional = + cachedInputTokens.getOptional("cached_input_tokens") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun reasoningTokens(): Optional = + reasoningTokens.getOptional("reasoning_tokens") + + /** + * Returns the raw JSON value of [inferenceTimeMs]. + * + * Unlike [inferenceTimeMs], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("inference_time_ms") + @ExcludeMissing + fun _inferenceTimeMs(): JsonField = inferenceTimeMs + + /** + * Returns the raw JSON value of [inputTokens]. + * + * Unlike [inputTokens], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("input_tokens") + @ExcludeMissing + fun _inputTokens(): JsonField = inputTokens + + /** + * Returns the raw JSON value of [outputTokens]. + * + * Unlike [outputTokens], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("output_tokens") + @ExcludeMissing + fun _outputTokens(): JsonField = outputTokens + + /** + * Returns the raw JSON value of [cachedInputTokens]. + * + * Unlike [cachedInputTokens], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("cached_input_tokens") + @ExcludeMissing + fun _cachedInputTokens(): JsonField = cachedInputTokens + + /** + * Returns the raw JSON value of [reasoningTokens]. + * + * Unlike [reasoningTokens], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("reasoning_tokens") + @ExcludeMissing + fun _reasoningTokens(): JsonField = reasoningTokens + + @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 [Usage]. + * + * The following fields are required: + * ```java + * .inferenceTimeMs() + * .inputTokens() + * .outputTokens() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Usage]. */ + class Builder internal constructor() { + + private var inferenceTimeMs: JsonField? = null + private var inputTokens: JsonField? = null + private var outputTokens: JsonField? = null + private var cachedInputTokens: JsonField = JsonMissing.of() + private var reasoningTokens: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(usage: Usage) = apply { + inferenceTimeMs = usage.inferenceTimeMs + inputTokens = usage.inputTokens + outputTokens = usage.outputTokens + cachedInputTokens = usage.cachedInputTokens + reasoningTokens = usage.reasoningTokens + additionalProperties = usage.additionalProperties.toMutableMap() + } + + fun inferenceTimeMs(inferenceTimeMs: Double) = + inferenceTimeMs(JsonField.of(inferenceTimeMs)) + + /** + * Sets [Builder.inferenceTimeMs] to an arbitrary JSON value. + * + * You should usually call [Builder.inferenceTimeMs] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun inferenceTimeMs(inferenceTimeMs: JsonField) = apply { + this.inferenceTimeMs = inferenceTimeMs + } + + fun inputTokens(inputTokens: Double) = inputTokens(JsonField.of(inputTokens)) + + /** + * Sets [Builder.inputTokens] to an arbitrary JSON value. + * + * You should usually call [Builder.inputTokens] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun inputTokens(inputTokens: JsonField) = apply { + this.inputTokens = inputTokens + } + + fun outputTokens(outputTokens: Double) = + outputTokens(JsonField.of(outputTokens)) + + /** + * Sets [Builder.outputTokens] to an arbitrary JSON value. + * + * You should usually call [Builder.outputTokens] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun outputTokens(outputTokens: JsonField) = apply { + this.outputTokens = outputTokens + } + + fun cachedInputTokens(cachedInputTokens: Double) = + cachedInputTokens(JsonField.of(cachedInputTokens)) + + /** + * Sets [Builder.cachedInputTokens] to an arbitrary JSON value. + * + * You should usually call [Builder.cachedInputTokens] with a well-typed + * [Double] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun cachedInputTokens(cachedInputTokens: JsonField) = apply { + this.cachedInputTokens = cachedInputTokens + } + + fun reasoningTokens(reasoningTokens: Double) = + reasoningTokens(JsonField.of(reasoningTokens)) + + /** + * Sets [Builder.reasoningTokens] to an arbitrary JSON value. + * + * You should usually call [Builder.reasoningTokens] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun reasoningTokens(reasoningTokens: JsonField) = apply { + this.reasoningTokens = reasoningTokens + } + + 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 [Usage]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .inferenceTimeMs() + * .inputTokens() + * .outputTokens() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Usage = + Usage( + checkRequired("inferenceTimeMs", inferenceTimeMs), + checkRequired("inputTokens", inputTokens), + checkRequired("outputTokens", outputTokens), + cachedInputTokens, + reasoningTokens, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Usage = apply { + if (validated) { + return@apply + } + + inferenceTimeMs() + inputTokens() + outputTokens() + cachedInputTokens() + reasoningTokens() + 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 (inferenceTimeMs.asKnown().isPresent) 1 else 0) + + (if (inputTokens.asKnown().isPresent) 1 else 0) + + (if (outputTokens.asKnown().isPresent) 1 else 0) + + (if (cachedInputTokens.asKnown().isPresent) 1 else 0) + + (if (reasoningTokens.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Usage && + inferenceTimeMs == other.inferenceTimeMs && + inputTokens == other.inputTokens && + outputTokens == other.outputTokens && + cachedInputTokens == other.cachedInputTokens && + reasoningTokens == other.reasoningTokens && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + inferenceTimeMs, + inputTokens, + outputTokens, + cachedInputTokens, + reasoningTokens, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Usage{inferenceTimeMs=$inferenceTimeMs, inputTokens=$inputTokens, outputTokens=$outputTokens, cachedInputTokens=$cachedInputTokens, reasoningTokens=$reasoningTokens, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Result && + actions == other.actions && + completed == other.completed && + message == other.message && + success == other.success && + metadata == other.metadata && + usage == other.usage && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + actions, + completed, + message, + success, + metadata, + usage, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Result{actions=$actions, completed=$completed, message=$message, success=$success, metadata=$metadata, usage=$usage, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Data && + result == other.result && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(result, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Data{result=$result, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SessionExecuteResponse && + data == other.data && + success == other.success && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(data, success, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "SessionExecuteResponse{data=$data, success=$success, 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 3833673..3a5c4bd 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 @@ -16,30 +16,43 @@ 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 java.time.OffsetDateTime +import java.time.format.DateTimeFormatter import java.util.Collections import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** - * Extracts data from the current page using natural language instructions and optional JSON schema - * for structured output. - */ +/** Extracts structured data from the current page using AI-powered analysis. */ class SessionExtractParams private constructor( - private val sessionId: String?, + private val id: String?, + private val xLanguage: XLanguage?, + private val xSdkVersion: String?, + private val xSentAt: OffsetDateTime?, private val xStreamResponse: XStreamResponse?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun sessionId(): Optional = Optional.ofNullable(sessionId) + /** Unique session identifier */ + fun id(): Optional = Optional.ofNullable(id) + /** Client SDK language */ + fun xLanguage(): Optional = Optional.ofNullable(xLanguage) + + /** Version of the Stagehand SDK */ + fun xSdkVersion(): Optional = Optional.ofNullable(xSdkVersion) + + /** ISO timestamp when request was sent */ + fun xSentAt(): Optional = Optional.ofNullable(xSentAt) + + /** Whether to stream the response via SSE */ fun xStreamResponse(): Optional = Optional.ofNullable(xStreamResponse) /** - * Frame ID to extract from + * Target frame ID for the extraction * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -47,7 +60,7 @@ private constructor( fun frameId(): Optional = body.frameId() /** - * Natural language instruction for extraction + * Natural language instruction for what to extract * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -61,7 +74,7 @@ private constructor( fun options(): Optional = body.options() /** - * JSON Schema for structured output + * JSON Schema defining the structure of data to extract * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -117,7 +130,10 @@ private constructor( /** A builder for [SessionExtractParams]. */ class Builder internal constructor() { - private var sessionId: String? = null + private var id: String? = null + private var xLanguage: XLanguage? = null + private var xSdkVersion: String? = null + private var xSentAt: OffsetDateTime? = null private var xStreamResponse: XStreamResponse? = null private var body: Body.Builder = Body.builder() private var additionalHeaders: Headers.Builder = Headers.builder() @@ -125,18 +141,41 @@ private constructor( @JvmSynthetic internal fun from(sessionExtractParams: SessionExtractParams) = apply { - sessionId = sessionExtractParams.sessionId + id = sessionExtractParams.id + xLanguage = sessionExtractParams.xLanguage + xSdkVersion = sessionExtractParams.xSdkVersion + xSentAt = sessionExtractParams.xSentAt xStreamResponse = sessionExtractParams.xStreamResponse body = sessionExtractParams.body.toBuilder() additionalHeaders = sessionExtractParams.additionalHeaders.toBuilder() additionalQueryParams = sessionExtractParams.additionalQueryParams.toBuilder() } - fun sessionId(sessionId: String?) = apply { this.sessionId = sessionId } + /** Unique session identifier */ + fun id(id: String?) = apply { this.id = id } + + /** Alias for calling [Builder.id] with `id.orElse(null)`. */ + fun id(id: Optional) = id(id.getOrNull()) + + /** Client SDK language */ + fun xLanguage(xLanguage: XLanguage?) = apply { this.xLanguage = xLanguage } + + /** Alias for calling [Builder.xLanguage] with `xLanguage.orElse(null)`. */ + fun xLanguage(xLanguage: Optional) = xLanguage(xLanguage.getOrNull()) + + /** Version of the Stagehand SDK */ + fun xSdkVersion(xSdkVersion: String?) = apply { this.xSdkVersion = xSdkVersion } + + /** Alias for calling [Builder.xSdkVersion] with `xSdkVersion.orElse(null)`. */ + fun xSdkVersion(xSdkVersion: Optional) = xSdkVersion(xSdkVersion.getOrNull()) - /** Alias for calling [Builder.sessionId] with `sessionId.orElse(null)`. */ - fun sessionId(sessionId: Optional) = sessionId(sessionId.getOrNull()) + /** ISO timestamp when request was sent */ + fun xSentAt(xSentAt: OffsetDateTime?) = apply { this.xSentAt = xSentAt } + /** Alias for calling [Builder.xSentAt] with `xSentAt.orElse(null)`. */ + fun xSentAt(xSentAt: Optional) = xSentAt(xSentAt.getOrNull()) + + /** Whether to stream the response via SSE */ fun xStreamResponse(xStreamResponse: XStreamResponse?) = apply { this.xStreamResponse = xStreamResponse } @@ -157,7 +196,7 @@ private constructor( */ fun body(body: Body) = apply { this.body = body.toBuilder() } - /** Frame ID to extract from */ + /** Target frame ID for the extraction */ fun frameId(frameId: String) = apply { body.frameId(frameId) } /** @@ -168,7 +207,7 @@ private constructor( */ fun frameId(frameId: JsonField) = apply { body.frameId(frameId) } - /** Natural language instruction for extraction */ + /** Natural language instruction for what to extract */ fun instruction(instruction: String) = apply { body.instruction(instruction) } /** @@ -190,7 +229,7 @@ private constructor( */ fun options(options: JsonField) = apply { body.options(options) } - /** JSON Schema for structured output */ + /** JSON Schema defining the structure of data to extract */ fun schema(schema: Schema) = apply { body.schema(schema) } /** @@ -325,7 +364,10 @@ private constructor( */ fun build(): SessionExtractParams = SessionExtractParams( - sessionId, + id, + xLanguage, + xSdkVersion, + xSentAt, xStreamResponse, body.build(), additionalHeaders.build(), @@ -337,13 +379,16 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> sessionId ?: "" + 0 -> id ?: "" else -> "" } override fun _headers(): Headers = Headers.builder() .apply { + xLanguage?.let { put("x-language", it.toString()) } + xSdkVersion?.let { put("x-sdk-version", it) } + xSentAt?.let { put("x-sent-at", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) } xStreamResponse?.let { put("x-stream-response", it.toString()) } putAll(additionalHeaders) } @@ -372,7 +417,7 @@ private constructor( ) : this(frameId, instruction, options, schema, mutableMapOf()) /** - * Frame ID to extract from + * Target frame ID for the extraction * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). @@ -380,7 +425,7 @@ private constructor( fun frameId(): Optional = frameId.getOptional("frameId") /** - * Natural language instruction for extraction + * Natural language instruction for what to extract * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). @@ -394,7 +439,7 @@ private constructor( fun options(): Optional = options.getOptional("options") /** - * JSON Schema for structured output + * JSON Schema defining the structure of data to extract * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). @@ -467,7 +512,7 @@ private constructor( additionalProperties = body.additionalProperties.toMutableMap() } - /** Frame ID to extract from */ + /** Target frame ID for the extraction */ fun frameId(frameId: String) = frameId(JsonField.of(frameId)) /** @@ -479,7 +524,7 @@ private constructor( */ fun frameId(frameId: JsonField) = apply { this.frameId = frameId } - /** Natural language instruction for extraction */ + /** Natural language instruction for what to extract */ fun instruction(instruction: String) = instruction(JsonField.of(instruction)) /** @@ -504,7 +549,7 @@ private constructor( */ fun options(options: JsonField) = apply { this.options = options } - /** JSON Schema for structured output */ + /** JSON Schema defining the structure of data to extract */ fun schema(schema: Schema) = schema(JsonField.of(schema)) /** @@ -607,7 +652,7 @@ private constructor( private constructor( private val model: JsonField, private val selector: JsonField, - private val timeout: JsonField, + private val timeout: JsonField, private val additionalProperties: MutableMap, ) { @@ -617,17 +662,20 @@ private constructor( @JsonProperty("selector") @ExcludeMissing selector: JsonField = JsonMissing.of(), - @JsonProperty("timeout") @ExcludeMissing timeout: JsonField = JsonMissing.of(), + @JsonProperty("timeout") @ExcludeMissing timeout: JsonField = JsonMissing.of(), ) : this(model, selector, timeout, mutableMapOf()) /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ fun model(): Optional = model.getOptional("model") /** - * Extract only from elements matching this selector + * CSS selector to scope extraction to a specific element * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). @@ -635,10 +683,12 @@ private constructor( fun selector(): Optional = selector.getOptional("selector") /** + * Timeout in ms for the extraction + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun timeout(): Optional = timeout.getOptional("timeout") + fun timeout(): Optional = timeout.getOptional("timeout") /** * Returns the raw JSON value of [model]. @@ -659,7 +709,7 @@ private constructor( * * Unlike [timeout], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("timeout") @ExcludeMissing fun _timeout(): JsonField = timeout + @JsonProperty("timeout") @ExcludeMissing fun _timeout(): JsonField = timeout @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -684,7 +734,7 @@ private constructor( private var model: JsonField = JsonMissing.of() private var selector: JsonField = JsonMissing.of() - private var timeout: JsonField = JsonMissing.of() + private var timeout: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic @@ -695,6 +745,10 @@ private constructor( additionalProperties = options.additionalProperties.toMutableMap() } + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') + */ fun model(model: ModelConfig) = model(JsonField.of(model)) /** @@ -706,7 +760,16 @@ private constructor( */ fun model(model: JsonField) = apply { this.model = model } - /** Extract only from elements matching this selector */ + /** Alias for calling [model] with `ModelConfig.ofName(name)`. */ + fun model(name: String) = model(ModelConfig.ofName(name)) + + /** + * Alias for calling [model] with `ModelConfig.ofModelConfigObject(modelConfigObject)`. + */ + fun model(modelConfigObject: ModelConfig.ModelConfigObject) = + model(ModelConfig.ofModelConfigObject(modelConfigObject)) + + /** CSS selector to scope extraction to a specific element */ fun selector(selector: String) = selector(JsonField.of(selector)) /** @@ -718,16 +781,17 @@ private constructor( */ fun selector(selector: JsonField) = apply { this.selector = selector } - fun timeout(timeout: Long) = timeout(JsonField.of(timeout)) + /** Timeout in ms for the extraction */ + fun timeout(timeout: Double) = timeout(JsonField.of(timeout)) /** * Sets [Builder.timeout] to an arbitrary JSON value. * - * You should usually call [Builder.timeout] with a well-typed [Long] value instead. + * You should usually call [Builder.timeout] with a well-typed [Double] value instead. * This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun timeout(timeout: JsonField) = apply { this.timeout = timeout } + fun timeout(timeout: JsonField) = apply { this.timeout = timeout } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -812,7 +876,7 @@ private constructor( "Options{model=$model, selector=$selector, timeout=$timeout, additionalProperties=$additionalProperties}" } - /** JSON Schema for structured output */ + /** JSON Schema defining the structure of data to extract */ class Schema @JsonCreator private constructor( @@ -912,6 +976,143 @@ private constructor( override fun toString() = "Schema{additionalProperties=$additionalProperties}" } + /** Client SDK language */ + class XLanguage @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 TYPESCRIPT = of("typescript") + + @JvmField val PYTHON = of("python") + + @JvmField val PLAYGROUND = of("playground") + + @JvmStatic fun of(value: String) = XLanguage(JsonField.of(value)) + } + + /** An enum containing [XLanguage]'s known values. */ + enum class Known { + TYPESCRIPT, + PYTHON, + PLAYGROUND, + } + + /** + * An enum containing [XLanguage]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [XLanguage] 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 { + TYPESCRIPT, + PYTHON, + PLAYGROUND, + /** + * An enum member indicating that [XLanguage] 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) { + TYPESCRIPT -> Value.TYPESCRIPT + PYTHON -> Value.PYTHON + PLAYGROUND -> Value.PLAYGROUND + 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) { + TYPESCRIPT -> Known.TYPESCRIPT + PYTHON -> Known.PYTHON + PLAYGROUND -> Known.PLAYGROUND + else -> throw StagehandInvalidDataException("Unknown XLanguage: $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 + + fun validate(): XLanguage = 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 XLanguage && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Whether to stream the response via SSE */ class XStreamResponse @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -1049,7 +1250,10 @@ private constructor( } return other is SessionExtractParams && - sessionId == other.sessionId && + id == other.id && + xLanguage == other.xLanguage && + xSdkVersion == other.xSdkVersion && + xSentAt == other.xSentAt && xStreamResponse == other.xStreamResponse && body == other.body && additionalHeaders == other.additionalHeaders && @@ -1057,8 +1261,17 @@ private constructor( } override fun hashCode(): Int = - Objects.hash(sessionId, xStreamResponse, body, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + xLanguage, + xSdkVersion, + xSentAt, + xStreamResponse, + body, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "SessionExtractParams{sessionId=$sessionId, xStreamResponse=$xStreamResponse, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "SessionExtractParams{id=$id, xLanguage=$xLanguage, xSdkVersion=$xSdkVersion, xSentAt=$xSentAt, xStreamResponse=$xStreamResponse, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractResponse.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractResponse.kt index 6816259..e281560 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractResponse.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractResponse.kt @@ -2,245 +2,224 @@ package com.browserbase.api.models.sessions -import com.browserbase.api.core.BaseDeserializer -import com.browserbase.api.core.BaseSerializer 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.getOrThrow -import com.browserbase.api.core.toImmutable +import com.browserbase.api.core.checkRequired import com.browserbase.api.errors.StagehandInvalidDataException import com.fasterxml.jackson.annotation.JsonAnyGetter import com.fasterxml.jackson.annotation.JsonAnySetter import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.core.ObjectCodec -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.SerializerProvider -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import com.fasterxml.jackson.databind.annotation.JsonSerialize -import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull -/** Default extraction result */ -@JsonDeserialize(using = SessionExtractResponse.Deserializer::class) -@JsonSerialize(using = SessionExtractResponse.Serializer::class) class SessionExtractResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val extraction: Extraction? = null, - private val custom: Custom? = null, - private val _json: JsonValue? = null, + private val data: JsonField, + private val success: JsonField, + private val additionalProperties: MutableMap, ) { - /** Default extraction result */ - fun extraction(): Optional = Optional.ofNullable(extraction) - - /** Structured data matching provided schema */ - fun custom(): Optional = Optional.ofNullable(custom) - - fun isExtraction(): Boolean = extraction != null + @JsonCreator + private constructor( + @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(), + @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(), + ) : this(data, success, mutableMapOf()) - fun isCustom(): Boolean = custom != null + /** + * @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 data(): Data = data.getRequired("data") - /** Default extraction result */ - fun asExtraction(): Extraction = extraction.getOrThrow("extraction") + /** + * Indicates whether the request was successful + * + * @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 success(): Boolean = success.getRequired("success") - /** Structured data matching provided schema */ - fun asCustom(): Custom = custom.getOrThrow("custom") + /** + * Returns the raw JSON value of [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField = data - fun _json(): Optional = Optional.ofNullable(_json) + /** + * Returns the raw JSON value of [success]. + * + * Unlike [success], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success - fun accept(visitor: Visitor): T = - when { - extraction != null -> visitor.visitExtraction(extraction) - custom != null -> visitor.visitCustom(custom) - else -> visitor.unknown(_json) - } + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } - private var validated: Boolean = false + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - fun validate(): SessionExtractResponse = apply { - if (validated) { - return@apply - } + fun toBuilder() = Builder().from(this) - accept( - object : Visitor { - override fun visitExtraction(extraction: Extraction) { - extraction.validate() - } + companion object { - override fun visitCustom(custom: Custom) { - custom.validate() - } - } - ) - validated = true + /** + * Returns a mutable builder for constructing an instance of [SessionExtractResponse]. + * + * The following fields are required: + * ```java + * .data() + * .success() + * ``` + */ + @JvmStatic fun builder() = Builder() } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: StagehandInvalidDataException) { - false - } + /** A builder for [SessionExtractResponse]. */ + class Builder internal constructor() { - /** - * Returns a score indicating how many valid values are contained in this object recursively. - * - * Used for best match union deserialization. - */ - @JvmSynthetic - internal fun validity(): Int = - accept( - object : Visitor { - override fun visitExtraction(extraction: Extraction) = extraction.validity() - - override fun visitCustom(custom: Custom) = custom.validity() + private var data: JsonField? = null + private var success: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() - override fun unknown(json: JsonValue?) = 0 - } - ) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + @JvmSynthetic + internal fun from(sessionExtractResponse: SessionExtractResponse) = apply { + data = sessionExtractResponse.data + success = sessionExtractResponse.success + additionalProperties = sessionExtractResponse.additionalProperties.toMutableMap() } - return other is SessionExtractResponse && - extraction == other.extraction && - custom == other.custom - } + fun data(data: Data) = data(JsonField.of(data)) - override fun hashCode(): Int = Objects.hash(extraction, custom) + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed [Data] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun data(data: JsonField) = apply { this.data = data } - override fun toString(): String = - when { - extraction != null -> "SessionExtractResponse{extraction=$extraction}" - custom != null -> "SessionExtractResponse{custom=$custom}" - _json != null -> "SessionExtractResponse{_unknown=$_json}" - else -> throw IllegalStateException("Invalid SessionExtractResponse") - } + /** Indicates whether the request was successful */ + fun success(success: Boolean) = success(JsonField.of(success)) - companion object { + /** + * Sets [Builder.success] to an arbitrary JSON value. + * + * You should usually call [Builder.success] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun success(success: JsonField) = apply { this.success = success } - /** Default extraction result */ - @JvmStatic - fun ofExtraction(extraction: Extraction) = SessionExtractResponse(extraction = extraction) + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - /** Structured data matching provided schema */ - @JvmStatic fun ofCustom(custom: Custom) = SessionExtractResponse(custom = custom) - } + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } - /** - * An interface that defines how to map each variant of [SessionExtractResponse] to a value of - * type [T]. - */ - interface Visitor { + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } - /** Default extraction result */ - fun visitExtraction(extraction: Extraction): T + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } - /** Structured data matching provided schema */ - fun visitCustom(custom: Custom): T + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } /** - * Maps an unknown variant of [SessionExtractResponse] to a value of type [T]. + * Returns an immutable instance of [SessionExtractResponse]. * - * An instance of [SessionExtractResponse] 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. + * Further updates to this [Builder] will not mutate the returned instance. * - * @throws StagehandInvalidDataException in the default implementation. + * The following fields are required: + * ```java + * .data() + * .success() + * ``` + * + * @throws IllegalStateException if any required field is unset. */ - fun unknown(json: JsonValue?): T { - throw StagehandInvalidDataException("Unknown SessionExtractResponse: $json") - } + fun build(): SessionExtractResponse = + SessionExtractResponse( + checkRequired("data", data), + checkRequired("success", success), + additionalProperties.toMutableMap(), + ) } - internal class Deserializer : - BaseDeserializer(SessionExtractResponse::class) { - - override fun ObjectCodec.deserialize(node: JsonNode): SessionExtractResponse { - val json = JsonValue.fromJsonNode(node) - - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef())?.let { - SessionExtractResponse(extraction = it, _json = json) - }, - tryDeserialize(node, jacksonTypeRef())?.let { - SessionExtractResponse(custom = 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 -> SessionExtractResponse(_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 var validated: Boolean = false + + fun validate(): SessionExtractResponse = apply { + if (validated) { + return@apply } + + data().validate() + success() + validated = true } - internal class Serializer : - BaseSerializer(SessionExtractResponse::class) { - - override fun serialize( - value: SessionExtractResponse, - generator: JsonGenerator, - provider: SerializerProvider, - ) { - when { - value.extraction != null -> generator.writeObject(value.extraction) - value.custom != null -> generator.writeObject(value.custom) - value._json != null -> generator.writeObject(value._json) - else -> throw IllegalStateException("Invalid SessionExtractResponse") - } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + false } - } - /** Default extraction result */ - class Extraction + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (data.asKnown().getOrNull()?.validity() ?: 0) + (if (success.asKnown().isPresent) 1 else 0) + + class Data @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val extraction: JsonField, + private val result: JsonValue, + private val actionId: JsonField, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("extraction") - @ExcludeMissing - extraction: JsonField = JsonMissing.of() - ) : this(extraction, mutableMapOf()) + @JsonProperty("result") @ExcludeMissing result: JsonValue = JsonMissing.of(), + @JsonProperty("actionId") @ExcludeMissing actionId: JsonField = JsonMissing.of(), + ) : this(result, actionId, mutableMapOf()) + + /** Extracted data matching the requested schema */ + @JsonProperty("result") @ExcludeMissing fun _result(): JsonValue = result /** + * Action ID for tracking + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun extraction(): Optional = extraction.getOptional("extraction") + fun actionId(): Optional = actionId.getOptional("actionId") /** - * Returns the raw JSON value of [extraction]. + * Returns the raw JSON value of [actionId]. * - * Unlike [extraction], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [actionId], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("extraction") - @ExcludeMissing - fun _extraction(): JsonField = extraction + @JsonProperty("actionId") @ExcludeMissing fun _actionId(): JsonField = actionId @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -256,32 +235,45 @@ private constructor( companion object { - /** Returns a mutable builder for constructing an instance of [Extraction]. */ + /** + * Returns a mutable builder for constructing an instance of [Data]. + * + * The following fields are required: + * ```java + * .result() + * ``` + */ @JvmStatic fun builder() = Builder() } - /** A builder for [Extraction]. */ + /** A builder for [Data]. */ class Builder internal constructor() { - private var extraction: JsonField = JsonMissing.of() + private var result: JsonValue? = null + private var actionId: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic - internal fun from(extraction: Extraction) = apply { - this.extraction = extraction.extraction - additionalProperties = extraction.additionalProperties.toMutableMap() + internal fun from(data: Data) = apply { + result = data.result + actionId = data.actionId + additionalProperties = data.additionalProperties.toMutableMap() } - fun extraction(extraction: String) = extraction(JsonField.of(extraction)) + /** Extracted data matching the requested schema */ + fun result(result: JsonValue) = apply { this.result = result } + + /** Action ID for tracking */ + fun actionId(actionId: String) = actionId(JsonField.of(actionId)) /** - * Sets [Builder.extraction] to an arbitrary JSON value. + * Sets [Builder.actionId] to an arbitrary JSON value. * - * You should usually call [Builder.extraction] 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.actionId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun extraction(extraction: JsonField) = apply { this.extraction = extraction } + fun actionId(actionId: JsonField) = apply { this.actionId = actionId } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -303,21 +295,29 @@ private constructor( } /** - * Returns an immutable instance of [Extraction]. + * Returns an immutable instance of [Data]. * * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .result() + * ``` + * + * @throws IllegalStateException if any required field is unset. */ - fun build(): Extraction = Extraction(extraction, additionalProperties.toMutableMap()) + fun build(): Data = + Data(checkRequired("result", result), actionId, additionalProperties.toMutableMap()) } private var validated: Boolean = false - fun validate(): Extraction = apply { + fun validate(): Data = apply { if (validated) { return@apply } - extraction() + actionId() validated = true } @@ -335,123 +335,42 @@ private constructor( * * Used for best match union deserialization. */ - @JvmSynthetic internal fun validity(): Int = (if (extraction.asKnown().isPresent) 1 else 0) + @JvmSynthetic internal fun validity(): Int = (if (actionId.asKnown().isPresent) 1 else 0) override fun equals(other: Any?): Boolean { if (this === other) { return true } - return other is Extraction && - extraction == other.extraction && + return other is Data && + result == other.result && + actionId == other.actionId && additionalProperties == other.additionalProperties } - private val hashCode: Int by lazy { Objects.hash(extraction, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(result, actionId, additionalProperties) } override fun hashCode(): Int = hashCode override fun toString() = - "Extraction{extraction=$extraction, additionalProperties=$additionalProperties}" + "Data{result=$result, actionId=$actionId, additionalProperties=$additionalProperties}" } - /** Structured data matching provided schema */ - class Custom - @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 [Custom]. */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [Custom]. */ - class Builder internal constructor() { - - private var additionalProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(custom: Custom) = apply { - additionalProperties = custom.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 [Custom]. - * - * Further updates to this [Builder] will not mutate the returned instance. - */ - fun build(): Custom = Custom(additionalProperties.toImmutable()) - } - - private var validated: Boolean = false - - fun validate(): Custom = apply { - if (validated) { - return@apply - } - - validated = true + override fun equals(other: Any?): Boolean { + if (this === other) { + return 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 Custom && additionalProperties == other.additionalProperties - } + return other is SessionExtractResponse && + data == other.data && + success == other.success && + additionalProperties == other.additionalProperties + } - private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(data, success, additionalProperties) } - override fun hashCode(): Int = hashCode + override fun hashCode(): Int = hashCode - override fun toString() = "Custom{additionalProperties=$additionalProperties}" - } + override fun toString() = + "SessionExtractResponse{data=$data, success=$success, additionalProperties=$additionalProperties}" } diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateParams.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateParams.kt index e57efd3..112be92 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateParams.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateParams.kt @@ -16,23 +16,39 @@ 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 java.time.OffsetDateTime +import java.time.format.DateTimeFormatter import java.util.Collections import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** Navigates the browser to the specified URL and waits for page load. */ +/** Navigates the browser to the specified URL. */ class SessionNavigateParams private constructor( - private val sessionId: String?, + private val id: String?, + private val xLanguage: XLanguage?, + private val xSdkVersion: String?, + private val xSentAt: OffsetDateTime?, private val xStreamResponse: XStreamResponse?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun sessionId(): Optional = Optional.ofNullable(sessionId) + /** Unique session identifier */ + fun id(): Optional = Optional.ofNullable(id) + /** Client SDK language */ + fun xLanguage(): Optional = Optional.ofNullable(xLanguage) + + /** Version of the Stagehand SDK */ + fun xSdkVersion(): Optional = Optional.ofNullable(xSdkVersion) + + /** ISO timestamp when request was sent */ + fun xSentAt(): Optional = Optional.ofNullable(xSentAt) + + /** Whether to stream the response via SSE */ fun xStreamResponse(): Optional = Optional.ofNullable(xStreamResponse) /** @@ -44,6 +60,8 @@ private constructor( fun url(): String = body.url() /** + * Target frame ID for the navigation + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ @@ -55,6 +73,14 @@ private constructor( */ fun options(): Optional = body.options() + /** + * Whether to stream the response via SSE + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun streamResponse(): Optional = body.streamResponse() + /** * Returns the raw JSON value of [url]. * @@ -76,6 +102,13 @@ private constructor( */ fun _options(): JsonField = body._options() + /** + * Returns the raw JSON value of [streamResponse]. + * + * Unlike [streamResponse], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _streamResponse(): JsonField = body._streamResponse() + fun _additionalBodyProperties(): Map = body._additionalProperties() /** Additional headers to send with the request. */ @@ -102,7 +135,10 @@ private constructor( /** A builder for [SessionNavigateParams]. */ class Builder internal constructor() { - private var sessionId: String? = null + private var id: String? = null + private var xLanguage: XLanguage? = null + private var xSdkVersion: String? = null + private var xSentAt: OffsetDateTime? = null private var xStreamResponse: XStreamResponse? = null private var body: Body.Builder = Body.builder() private var additionalHeaders: Headers.Builder = Headers.builder() @@ -110,18 +146,41 @@ private constructor( @JvmSynthetic internal fun from(sessionNavigateParams: SessionNavigateParams) = apply { - sessionId = sessionNavigateParams.sessionId + id = sessionNavigateParams.id + xLanguage = sessionNavigateParams.xLanguage + xSdkVersion = sessionNavigateParams.xSdkVersion + xSentAt = sessionNavigateParams.xSentAt xStreamResponse = sessionNavigateParams.xStreamResponse body = sessionNavigateParams.body.toBuilder() additionalHeaders = sessionNavigateParams.additionalHeaders.toBuilder() additionalQueryParams = sessionNavigateParams.additionalQueryParams.toBuilder() } - fun sessionId(sessionId: String?) = apply { this.sessionId = sessionId } + /** Unique session identifier */ + fun id(id: String?) = apply { this.id = id } + + /** Alias for calling [Builder.id] with `id.orElse(null)`. */ + fun id(id: Optional) = id(id.getOrNull()) + + /** Client SDK language */ + fun xLanguage(xLanguage: XLanguage?) = apply { this.xLanguage = xLanguage } + + /** Alias for calling [Builder.xLanguage] with `xLanguage.orElse(null)`. */ + fun xLanguage(xLanguage: Optional) = xLanguage(xLanguage.getOrNull()) + + /** Version of the Stagehand SDK */ + fun xSdkVersion(xSdkVersion: String?) = apply { this.xSdkVersion = xSdkVersion } + + /** Alias for calling [Builder.xSdkVersion] with `xSdkVersion.orElse(null)`. */ + fun xSdkVersion(xSdkVersion: Optional) = xSdkVersion(xSdkVersion.getOrNull()) - /** Alias for calling [Builder.sessionId] with `sessionId.orElse(null)`. */ - fun sessionId(sessionId: Optional) = sessionId(sessionId.getOrNull()) + /** ISO timestamp when request was sent */ + fun xSentAt(xSentAt: OffsetDateTime?) = apply { this.xSentAt = xSentAt } + /** Alias for calling [Builder.xSentAt] with `xSentAt.orElse(null)`. */ + fun xSentAt(xSentAt: Optional) = xSentAt(xSentAt.getOrNull()) + + /** Whether to stream the response via SSE */ fun xStreamResponse(xStreamResponse: XStreamResponse?) = apply { this.xStreamResponse = xStreamResponse } @@ -138,6 +197,7 @@ private constructor( * - [url] * - [frameId] * - [options] + * - [streamResponse] */ fun body(body: Body) = apply { this.body = body.toBuilder() } @@ -152,6 +212,7 @@ private constructor( */ fun url(url: JsonField) = apply { body.url(url) } + /** Target frame ID for the navigation */ fun frameId(frameId: String) = apply { body.frameId(frameId) } /** @@ -172,6 +233,20 @@ private constructor( */ fun options(options: JsonField) = apply { body.options(options) } + /** Whether to stream the response via SSE */ + fun streamResponse(streamResponse: Boolean) = apply { body.streamResponse(streamResponse) } + + /** + * Sets [Builder.streamResponse] to an arbitrary JSON value. + * + * You should usually call [Builder.streamResponse] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun streamResponse(streamResponse: JsonField) = apply { + body.streamResponse(streamResponse) + } + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { body.additionalProperties(additionalBodyProperties) } @@ -303,7 +378,10 @@ private constructor( */ fun build(): SessionNavigateParams = SessionNavigateParams( - sessionId, + id, + xLanguage, + xSdkVersion, + xSentAt, xStreamResponse, body.build(), additionalHeaders.build(), @@ -315,13 +393,16 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> sessionId ?: "" + 0 -> id ?: "" else -> "" } override fun _headers(): Headers = Headers.builder() .apply { + xLanguage?.let { put("x-language", it.toString()) } + xSdkVersion?.let { put("x-sdk-version", it) } + xSentAt?.let { put("x-sent-at", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) } xStreamResponse?.let { put("x-stream-response", it.toString()) } putAll(additionalHeaders) } @@ -335,6 +416,7 @@ private constructor( private val url: JsonField, private val frameId: JsonField, private val options: JsonField, + private val streamResponse: JsonField, private val additionalProperties: MutableMap, ) { @@ -343,7 +425,10 @@ private constructor( @JsonProperty("url") @ExcludeMissing url: JsonField = JsonMissing.of(), @JsonProperty("frameId") @ExcludeMissing frameId: JsonField = JsonMissing.of(), @JsonProperty("options") @ExcludeMissing options: JsonField = JsonMissing.of(), - ) : this(url, frameId, options, mutableMapOf()) + @JsonProperty("streamResponse") + @ExcludeMissing + streamResponse: JsonField = JsonMissing.of(), + ) : this(url, frameId, options, streamResponse, mutableMapOf()) /** * URL to navigate to @@ -354,6 +439,8 @@ private constructor( fun url(): String = url.getRequired("url") /** + * Target frame ID for the navigation + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ @@ -365,6 +452,14 @@ private constructor( */ fun options(): Optional = options.getOptional("options") + /** + * Whether to stream the response via SSE + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun streamResponse(): Optional = streamResponse.getOptional("streamResponse") + /** * Returns the raw JSON value of [url]. * @@ -386,6 +481,16 @@ private constructor( */ @JsonProperty("options") @ExcludeMissing fun _options(): JsonField = options + /** + * Returns the raw JSON value of [streamResponse]. + * + * Unlike [streamResponse], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("streamResponse") + @ExcludeMissing + fun _streamResponse(): JsonField = streamResponse + @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { additionalProperties.put(key, value) @@ -417,6 +522,7 @@ private constructor( private var url: JsonField? = null private var frameId: JsonField = JsonMissing.of() private var options: JsonField = JsonMissing.of() + private var streamResponse: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic @@ -424,6 +530,7 @@ private constructor( url = body.url frameId = body.frameId options = body.options + streamResponse = body.streamResponse additionalProperties = body.additionalProperties.toMutableMap() } @@ -439,6 +546,7 @@ private constructor( */ fun url(url: JsonField) = apply { this.url = url } + /** Target frame ID for the navigation */ fun frameId(frameId: String) = frameId(JsonField.of(frameId)) /** @@ -461,6 +569,21 @@ private constructor( */ fun options(options: JsonField) = apply { this.options = options } + /** Whether to stream the response via SSE */ + fun streamResponse(streamResponse: Boolean) = + streamResponse(JsonField.of(streamResponse)) + + /** + * Sets [Builder.streamResponse] to an arbitrary JSON value. + * + * You should usually call [Builder.streamResponse] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun streamResponse(streamResponse: JsonField) = apply { + this.streamResponse = streamResponse + } + fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() putAllAdditionalProperties(additionalProperties) @@ -497,6 +620,7 @@ private constructor( checkRequired("url", url), frameId, options, + streamResponse, additionalProperties.toMutableMap(), ) } @@ -511,6 +635,7 @@ private constructor( url() frameId() options().ifPresent { it.validate() } + streamResponse() validated = true } @@ -532,7 +657,8 @@ private constructor( internal fun validity(): Int = (if (url.asKnown().isPresent) 1 else 0) + (if (frameId.asKnown().isPresent) 1 else 0) + - (options.asKnown().getOrNull()?.validity() ?: 0) + (options.asKnown().getOrNull()?.validity() ?: 0) + + (if (streamResponse.asKnown().isPresent) 1 else 0) override fun equals(other: Any?): Boolean { if (this === other) { @@ -543,32 +669,53 @@ private constructor( url == other.url && frameId == other.frameId && options == other.options && + streamResponse == other.streamResponse && additionalProperties == other.additionalProperties } private val hashCode: Int by lazy { - Objects.hash(url, frameId, options, additionalProperties) + Objects.hash(url, frameId, options, streamResponse, additionalProperties) } override fun hashCode(): Int = hashCode override fun toString() = - "Body{url=$url, frameId=$frameId, options=$options, additionalProperties=$additionalProperties}" + "Body{url=$url, frameId=$frameId, options=$options, streamResponse=$streamResponse, additionalProperties=$additionalProperties}" } class Options @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( + private val referer: JsonField, + private val timeout: JsonField, private val waitUntil: JsonField, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( + @JsonProperty("referer") @ExcludeMissing referer: JsonField = JsonMissing.of(), + @JsonProperty("timeout") @ExcludeMissing timeout: JsonField = JsonMissing.of(), @JsonProperty("waitUntil") @ExcludeMissing - waitUntil: JsonField = JsonMissing.of() - ) : this(waitUntil, mutableMapOf()) + waitUntil: JsonField = JsonMissing.of(), + ) : this(referer, timeout, waitUntil, mutableMapOf()) + + /** + * Referer header to send with the request + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun referer(): Optional = referer.getOptional("referer") + + /** + * Timeout in ms for the navigation + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun timeout(): Optional = timeout.getOptional("timeout") /** * When to consider navigation complete @@ -578,6 +725,20 @@ private constructor( */ fun waitUntil(): Optional = waitUntil.getOptional("waitUntil") + /** + * Returns the raw JSON value of [referer]. + * + * Unlike [referer], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("referer") @ExcludeMissing fun _referer(): JsonField = referer + + /** + * Returns the raw JSON value of [timeout]. + * + * Unlike [timeout], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("timeout") @ExcludeMissing fun _timeout(): JsonField = timeout + /** * Returns the raw JSON value of [waitUntil]. * @@ -608,15 +769,43 @@ private constructor( /** A builder for [Options]. */ class Builder internal constructor() { + private var referer: JsonField = JsonMissing.of() + private var timeout: JsonField = JsonMissing.of() private var waitUntil: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(options: Options) = apply { + referer = options.referer + timeout = options.timeout waitUntil = options.waitUntil additionalProperties = options.additionalProperties.toMutableMap() } + /** Referer header to send with the request */ + fun referer(referer: String) = referer(JsonField.of(referer)) + + /** + * Sets [Builder.referer] to an arbitrary JSON value. + * + * You should usually call [Builder.referer] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun referer(referer: JsonField) = apply { this.referer = referer } + + /** Timeout in ms for the navigation */ + fun timeout(timeout: Double) = timeout(JsonField.of(timeout)) + + /** + * Sets [Builder.timeout] to an arbitrary JSON value. + * + * You should usually call [Builder.timeout] with a well-typed [Double] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun timeout(timeout: JsonField) = apply { this.timeout = timeout } + /** When to consider navigation complete */ fun waitUntil(waitUntil: WaitUntil) = waitUntil(JsonField.of(waitUntil)) @@ -653,7 +842,8 @@ private constructor( * * Further updates to this [Builder] will not mutate the returned instance. */ - fun build(): Options = Options(waitUntil, additionalProperties.toMutableMap()) + fun build(): Options = + Options(referer, timeout, waitUntil, additionalProperties.toMutableMap()) } private var validated: Boolean = false @@ -663,6 +853,8 @@ private constructor( return@apply } + referer() + timeout() waitUntil().ifPresent { it.validate() } validated = true } @@ -682,7 +874,10 @@ private constructor( * Used for best match union deserialization. */ @JvmSynthetic - internal fun validity(): Int = (waitUntil.asKnown().getOrNull()?.validity() ?: 0) + internal fun validity(): Int = + (if (referer.asKnown().isPresent) 1 else 0) + + (if (timeout.asKnown().isPresent) 1 else 0) + + (waitUntil.asKnown().getOrNull()?.validity() ?: 0) /** When to consider navigation complete */ class WaitUntil @JsonCreator private constructor(private val value: JsonField) : @@ -828,18 +1023,159 @@ private constructor( } return other is Options && + referer == other.referer && + timeout == other.timeout && waitUntil == other.waitUntil && additionalProperties == other.additionalProperties } - private val hashCode: Int by lazy { Objects.hash(waitUntil, additionalProperties) } + private val hashCode: Int by lazy { + Objects.hash(referer, timeout, waitUntil, additionalProperties) + } override fun hashCode(): Int = hashCode override fun toString() = - "Options{waitUntil=$waitUntil, additionalProperties=$additionalProperties}" + "Options{referer=$referer, timeout=$timeout, waitUntil=$waitUntil, additionalProperties=$additionalProperties}" + } + + /** Client SDK language */ + class XLanguage @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 TYPESCRIPT = of("typescript") + + @JvmField val PYTHON = of("python") + + @JvmField val PLAYGROUND = of("playground") + + @JvmStatic fun of(value: String) = XLanguage(JsonField.of(value)) + } + + /** An enum containing [XLanguage]'s known values. */ + enum class Known { + TYPESCRIPT, + PYTHON, + PLAYGROUND, + } + + /** + * An enum containing [XLanguage]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [XLanguage] 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 { + TYPESCRIPT, + PYTHON, + PLAYGROUND, + /** + * An enum member indicating that [XLanguage] 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) { + TYPESCRIPT -> Value.TYPESCRIPT + PYTHON -> Value.PYTHON + PLAYGROUND -> Value.PLAYGROUND + 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) { + TYPESCRIPT -> Known.TYPESCRIPT + PYTHON -> Known.PYTHON + PLAYGROUND -> Known.PLAYGROUND + else -> throw StagehandInvalidDataException("Unknown XLanguage: $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 + + fun validate(): XLanguage = 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 XLanguage && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() } + /** Whether to stream the response via SSE */ class XStreamResponse @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -977,7 +1313,10 @@ private constructor( } return other is SessionNavigateParams && - sessionId == other.sessionId && + id == other.id && + xLanguage == other.xLanguage && + xSdkVersion == other.xSdkVersion && + xSentAt == other.xSentAt && xStreamResponse == other.xStreamResponse && body == other.body && additionalHeaders == other.additionalHeaders && @@ -985,8 +1324,17 @@ private constructor( } override fun hashCode(): Int = - Objects.hash(sessionId, xStreamResponse, body, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + xLanguage, + xSdkVersion, + xSentAt, + xStreamResponse, + body, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "SessionNavigateParams{sessionId=$sessionId, xStreamResponse=$xStreamResponse, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "SessionNavigateParams{id=$id, xLanguage=$xLanguage, xSdkVersion=$xSdkVersion, xSentAt=$xSentAt, xStreamResponse=$xStreamResponse, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponse.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponse.kt index 50422bd..2518477 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponse.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponse.kt @@ -6,6 +6,7 @@ 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.checkRequired import com.browserbase.api.errors.StagehandInvalidDataException import com.fasterxml.jackson.annotation.JsonAnyGetter import com.fasterxml.jackson.annotation.JsonAnySetter @@ -14,62 +15,49 @@ import com.fasterxml.jackson.annotation.JsonProperty import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull -/** Navigation response (may be null) */ class SessionNavigateResponse @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val ok: JsonField, - private val status: JsonField, - private val url: JsonField, + private val data: JsonField, + private val success: JsonField, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("ok") @ExcludeMissing ok: JsonField = JsonMissing.of(), - @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), - @JsonProperty("url") @ExcludeMissing url: JsonField = JsonMissing.of(), - ) : this(ok, status, url, mutableMapOf()) + @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(), + @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(), + ) : this(data, success, mutableMapOf()) /** - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected 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 ok(): Optional = ok.getOptional("ok") + fun data(): Data = data.getRequired("data") /** - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected value). - */ - fun status(): Optional = status.getOptional("status") - - /** - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected value). - */ - fun url(): Optional = url.getOptional("url") - - /** - * Returns the raw JSON value of [ok]. + * Indicates whether the request was successful * - * Unlike [ok], this method doesn't throw if the JSON field has an unexpected type. + * @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). */ - @JsonProperty("ok") @ExcludeMissing fun _ok(): JsonField = ok + fun success(): Boolean = success.getRequired("success") /** - * Returns the raw JSON value of [status]. + * Returns the raw JSON value of [data]. * - * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField = data /** - * Returns the raw JSON value of [url]. + * Returns the raw JSON value of [success]. * - * Unlike [url], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [success], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("url") @ExcludeMissing fun _url(): JsonField = url + @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -85,55 +73,52 @@ private constructor( companion object { - /** Returns a mutable builder for constructing an instance of [SessionNavigateResponse]. */ + /** + * Returns a mutable builder for constructing an instance of [SessionNavigateResponse]. + * + * The following fields are required: + * ```java + * .data() + * .success() + * ``` + */ @JvmStatic fun builder() = Builder() } /** A builder for [SessionNavigateResponse]. */ class Builder internal constructor() { - private var ok: JsonField = JsonMissing.of() - private var status: JsonField = JsonMissing.of() - private var url: JsonField = JsonMissing.of() + private var data: JsonField? = null + private var success: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(sessionNavigateResponse: SessionNavigateResponse) = apply { - ok = sessionNavigateResponse.ok - status = sessionNavigateResponse.status - url = sessionNavigateResponse.url + data = sessionNavigateResponse.data + success = sessionNavigateResponse.success additionalProperties = sessionNavigateResponse.additionalProperties.toMutableMap() } - fun ok(ok: Boolean) = ok(JsonField.of(ok)) - - /** - * Sets [Builder.ok] to an arbitrary JSON value. - * - * You should usually call [Builder.ok] with a well-typed [Boolean] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. - */ - fun ok(ok: JsonField) = apply { this.ok = ok } - - fun status(status: Long) = status(JsonField.of(status)) + fun data(data: Data) = data(JsonField.of(data)) /** - * Sets [Builder.status] to an arbitrary JSON value. + * Sets [Builder.data] to an arbitrary JSON value. * - * You should usually call [Builder.status] with a well-typed [Long] value instead. This + * You should usually call [Builder.data] with a well-typed [Data] value instead. This * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun status(status: JsonField) = apply { this.status = status } + fun data(data: JsonField) = apply { this.data = data } - fun url(url: String) = url(JsonField.of(url)) + /** Indicates whether the request was successful */ + fun success(success: Boolean) = success(JsonField.of(success)) /** - * Sets [Builder.url] to an arbitrary JSON value. + * Sets [Builder.success] to an arbitrary JSON value. * - * You should usually call [Builder.url] with a well-typed [String] value instead. This + * You should usually call [Builder.success] with a well-typed [Boolean] value instead. This * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun url(url: JsonField) = apply { this.url = url } + fun success(success: JsonField) = apply { this.success = success } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -158,9 +143,21 @@ private constructor( * Returns an immutable instance of [SessionNavigateResponse]. * * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .data() + * .success() + * ``` + * + * @throws IllegalStateException if any required field is unset. */ fun build(): SessionNavigateResponse = - SessionNavigateResponse(ok, status, url, additionalProperties.toMutableMap()) + SessionNavigateResponse( + checkRequired("data", data), + checkRequired("success", success), + additionalProperties.toMutableMap(), + ) } private var validated: Boolean = false @@ -170,9 +167,8 @@ private constructor( return@apply } - ok() - status() - url() + data().validate() + success() validated = true } @@ -191,9 +187,174 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (if (ok.asKnown().isPresent) 1 else 0) + - (if (status.asKnown().isPresent) 1 else 0) + - (if (url.asKnown().isPresent) 1 else 0) + (data.asKnown().getOrNull()?.validity() ?: 0) + (if (success.asKnown().isPresent) 1 else 0) + + class Data + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val result: JsonValue, + private val actionId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("result") @ExcludeMissing result: JsonValue = JsonMissing.of(), + @JsonProperty("actionId") @ExcludeMissing actionId: JsonField = JsonMissing.of(), + ) : this(result, actionId, mutableMapOf()) + + /** Navigation response (Playwright Response object or null) */ + @JsonProperty("result") @ExcludeMissing fun _result(): JsonValue = result + + /** + * Action ID for tracking + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun actionId(): Optional = actionId.getOptional("actionId") + + /** + * Returns the raw JSON value of [actionId]. + * + * Unlike [actionId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("actionId") @ExcludeMissing fun _actionId(): JsonField = actionId + + @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 [Data]. + * + * The following fields are required: + * ```java + * .result() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Data]. */ + class Builder internal constructor() { + + private var result: JsonValue? = null + private var actionId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(data: Data) = apply { + result = data.result + actionId = data.actionId + additionalProperties = data.additionalProperties.toMutableMap() + } + + /** Navigation response (Playwright Response object or null) */ + fun result(result: JsonValue) = apply { this.result = result } + + /** Action ID for tracking */ + fun actionId(actionId: String) = actionId(JsonField.of(actionId)) + + /** + * Sets [Builder.actionId] to an arbitrary JSON value. + * + * You should usually call [Builder.actionId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun actionId(actionId: JsonField) = apply { this.actionId = actionId } + + 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 [Data]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .result() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Data = + Data(checkRequired("result", result), actionId, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Data = apply { + if (validated) { + return@apply + } + + actionId() + 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 (actionId.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Data && + result == other.result && + actionId == other.actionId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(result, actionId, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Data{result=$result, actionId=$actionId, additionalProperties=$additionalProperties}" + } override fun equals(other: Any?): Boolean { if (this === other) { @@ -201,16 +362,15 @@ private constructor( } return other is SessionNavigateResponse && - ok == other.ok && - status == other.status && - url == other.url && + data == other.data && + success == other.success && additionalProperties == other.additionalProperties } - private val hashCode: Int by lazy { Objects.hash(ok, status, url, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(data, success, additionalProperties) } override fun hashCode(): Int = hashCode override fun toString() = - "SessionNavigateResponse{ok=$ok, status=$status, url=$url, additionalProperties=$additionalProperties}" + "SessionNavigateResponse{data=$data, success=$success, additionalProperties=$additionalProperties}" } 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 0d67973..66c3b91 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 @@ -15,30 +15,45 @@ 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 java.time.OffsetDateTime +import java.time.format.DateTimeFormatter import java.util.Collections import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull /** - * Returns a list of candidate actions that can be performed on the page, optionally filtered by - * natural language instruction. + * Identifies and returns available actions on the current page that match the given instruction. */ class SessionObserveParams private constructor( - private val sessionId: String?, + private val id: String?, + private val xLanguage: XLanguage?, + private val xSdkVersion: String?, + private val xSentAt: OffsetDateTime?, private val xStreamResponse: XStreamResponse?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { - fun sessionId(): Optional = Optional.ofNullable(sessionId) + /** Unique session identifier */ + fun id(): Optional = Optional.ofNullable(id) + /** Client SDK language */ + fun xLanguage(): Optional = Optional.ofNullable(xLanguage) + + /** Version of the Stagehand SDK */ + fun xSdkVersion(): Optional = Optional.ofNullable(xSdkVersion) + + /** ISO timestamp when request was sent */ + fun xSentAt(): Optional = Optional.ofNullable(xSentAt) + + /** Whether to stream the response via SSE */ fun xStreamResponse(): Optional = Optional.ofNullable(xStreamResponse) /** - * Frame ID to observe + * Target frame ID for the observation * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -46,7 +61,7 @@ private constructor( fun frameId(): Optional = body.frameId() /** - * Natural language instruction to filter actions + * Natural language instruction for what actions to find * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -101,7 +116,10 @@ private constructor( /** A builder for [SessionObserveParams]. */ class Builder internal constructor() { - private var sessionId: String? = null + private var id: String? = null + private var xLanguage: XLanguage? = null + private var xSdkVersion: String? = null + private var xSentAt: OffsetDateTime? = null private var xStreamResponse: XStreamResponse? = null private var body: Body.Builder = Body.builder() private var additionalHeaders: Headers.Builder = Headers.builder() @@ -109,18 +127,41 @@ private constructor( @JvmSynthetic internal fun from(sessionObserveParams: SessionObserveParams) = apply { - sessionId = sessionObserveParams.sessionId + id = sessionObserveParams.id + xLanguage = sessionObserveParams.xLanguage + xSdkVersion = sessionObserveParams.xSdkVersion + xSentAt = sessionObserveParams.xSentAt xStreamResponse = sessionObserveParams.xStreamResponse body = sessionObserveParams.body.toBuilder() additionalHeaders = sessionObserveParams.additionalHeaders.toBuilder() additionalQueryParams = sessionObserveParams.additionalQueryParams.toBuilder() } - fun sessionId(sessionId: String?) = apply { this.sessionId = sessionId } + /** Unique session identifier */ + fun id(id: String?) = apply { this.id = id } + + /** Alias for calling [Builder.id] with `id.orElse(null)`. */ + fun id(id: Optional) = id(id.getOrNull()) + + /** Client SDK language */ + fun xLanguage(xLanguage: XLanguage?) = apply { this.xLanguage = xLanguage } + + /** Alias for calling [Builder.xLanguage] with `xLanguage.orElse(null)`. */ + fun xLanguage(xLanguage: Optional) = xLanguage(xLanguage.getOrNull()) + + /** Version of the Stagehand SDK */ + fun xSdkVersion(xSdkVersion: String?) = apply { this.xSdkVersion = xSdkVersion } + + /** Alias for calling [Builder.xSdkVersion] with `xSdkVersion.orElse(null)`. */ + fun xSdkVersion(xSdkVersion: Optional) = xSdkVersion(xSdkVersion.getOrNull()) - /** Alias for calling [Builder.sessionId] with `sessionId.orElse(null)`. */ - fun sessionId(sessionId: Optional) = sessionId(sessionId.getOrNull()) + /** ISO timestamp when request was sent */ + fun xSentAt(xSentAt: OffsetDateTime?) = apply { this.xSentAt = xSentAt } + /** Alias for calling [Builder.xSentAt] with `xSentAt.orElse(null)`. */ + fun xSentAt(xSentAt: Optional) = xSentAt(xSentAt.getOrNull()) + + /** Whether to stream the response via SSE */ fun xStreamResponse(xStreamResponse: XStreamResponse?) = apply { this.xStreamResponse = xStreamResponse } @@ -140,7 +181,7 @@ private constructor( */ fun body(body: Body) = apply { this.body = body.toBuilder() } - /** Frame ID to observe */ + /** Target frame ID for the observation */ fun frameId(frameId: String) = apply { body.frameId(frameId) } /** @@ -151,7 +192,7 @@ private constructor( */ fun frameId(frameId: JsonField) = apply { body.frameId(frameId) } - /** Natural language instruction to filter actions */ + /** Natural language instruction for what actions to find */ fun instruction(instruction: String) = apply { body.instruction(instruction) } /** @@ -297,7 +338,10 @@ private constructor( */ fun build(): SessionObserveParams = SessionObserveParams( - sessionId, + id, + xLanguage, + xSdkVersion, + xSentAt, xStreamResponse, body.build(), additionalHeaders.build(), @@ -309,13 +353,16 @@ private constructor( fun _pathParam(index: Int): String = when (index) { - 0 -> sessionId ?: "" + 0 -> id ?: "" else -> "" } override fun _headers(): Headers = Headers.builder() .apply { + xLanguage?.let { put("x-language", it.toString()) } + xSdkVersion?.let { put("x-sdk-version", it) } + xSentAt?.let { put("x-sent-at", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) } xStreamResponse?.let { put("x-stream-response", it.toString()) } putAll(additionalHeaders) } @@ -342,7 +389,7 @@ private constructor( ) : this(frameId, instruction, options, mutableMapOf()) /** - * Frame ID to observe + * Target frame ID for the observation * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). @@ -350,7 +397,7 @@ private constructor( fun frameId(): Optional = frameId.getOptional("frameId") /** - * Natural language instruction to filter actions + * Natural language instruction for what actions to find * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). @@ -420,7 +467,7 @@ private constructor( additionalProperties = body.additionalProperties.toMutableMap() } - /** Frame ID to observe */ + /** Target frame ID for the observation */ fun frameId(frameId: String) = frameId(JsonField.of(frameId)) /** @@ -432,7 +479,7 @@ private constructor( */ fun frameId(frameId: JsonField) = apply { this.frameId = frameId } - /** Natural language instruction to filter actions */ + /** Natural language instruction for what actions to find */ fun instruction(instruction: String) = instruction(JsonField.of(instruction)) /** @@ -545,7 +592,7 @@ private constructor( private constructor( private val model: JsonField, private val selector: JsonField, - private val timeout: JsonField, + private val timeout: JsonField, private val additionalProperties: MutableMap, ) { @@ -555,17 +602,20 @@ private constructor( @JsonProperty("selector") @ExcludeMissing selector: JsonField = JsonMissing.of(), - @JsonProperty("timeout") @ExcludeMissing timeout: JsonField = JsonMissing.of(), + @JsonProperty("timeout") @ExcludeMissing timeout: JsonField = JsonMissing.of(), ) : this(model, selector, timeout, mutableMapOf()) /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ fun model(): Optional = model.getOptional("model") /** - * Observe only elements matching this selector + * CSS selector to scope observation to a specific element * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). @@ -573,10 +623,12 @@ private constructor( fun selector(): Optional = selector.getOptional("selector") /** + * Timeout in ms for the observation + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun timeout(): Optional = timeout.getOptional("timeout") + fun timeout(): Optional = timeout.getOptional("timeout") /** * Returns the raw JSON value of [model]. @@ -597,7 +649,7 @@ private constructor( * * Unlike [timeout], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("timeout") @ExcludeMissing fun _timeout(): JsonField = timeout + @JsonProperty("timeout") @ExcludeMissing fun _timeout(): JsonField = timeout @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -622,7 +674,7 @@ private constructor( private var model: JsonField = JsonMissing.of() private var selector: JsonField = JsonMissing.of() - private var timeout: JsonField = JsonMissing.of() + private var timeout: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic @@ -633,6 +685,10 @@ private constructor( additionalProperties = options.additionalProperties.toMutableMap() } + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') + */ fun model(model: ModelConfig) = model(JsonField.of(model)) /** @@ -644,7 +700,16 @@ private constructor( */ fun model(model: JsonField) = apply { this.model = model } - /** Observe only elements matching this selector */ + /** Alias for calling [model] with `ModelConfig.ofName(name)`. */ + fun model(name: String) = model(ModelConfig.ofName(name)) + + /** + * Alias for calling [model] with `ModelConfig.ofModelConfigObject(modelConfigObject)`. + */ + fun model(modelConfigObject: ModelConfig.ModelConfigObject) = + model(ModelConfig.ofModelConfigObject(modelConfigObject)) + + /** CSS selector to scope observation to a specific element */ fun selector(selector: String) = selector(JsonField.of(selector)) /** @@ -656,16 +721,17 @@ private constructor( */ fun selector(selector: JsonField) = apply { this.selector = selector } - fun timeout(timeout: Long) = timeout(JsonField.of(timeout)) + /** Timeout in ms for the observation */ + fun timeout(timeout: Double) = timeout(JsonField.of(timeout)) /** * Sets [Builder.timeout] to an arbitrary JSON value. * - * You should usually call [Builder.timeout] with a well-typed [Long] value instead. + * You should usually call [Builder.timeout] with a well-typed [Double] value instead. * This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun timeout(timeout: JsonField) = apply { this.timeout = timeout } + fun timeout(timeout: JsonField) = apply { this.timeout = timeout } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -750,6 +816,143 @@ private constructor( "Options{model=$model, selector=$selector, timeout=$timeout, additionalProperties=$additionalProperties}" } + /** Client SDK language */ + class XLanguage @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 TYPESCRIPT = of("typescript") + + @JvmField val PYTHON = of("python") + + @JvmField val PLAYGROUND = of("playground") + + @JvmStatic fun of(value: String) = XLanguage(JsonField.of(value)) + } + + /** An enum containing [XLanguage]'s known values. */ + enum class Known { + TYPESCRIPT, + PYTHON, + PLAYGROUND, + } + + /** + * An enum containing [XLanguage]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [XLanguage] 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 { + TYPESCRIPT, + PYTHON, + PLAYGROUND, + /** + * An enum member indicating that [XLanguage] 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) { + TYPESCRIPT -> Value.TYPESCRIPT + PYTHON -> Value.PYTHON + PLAYGROUND -> Value.PLAYGROUND + 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) { + TYPESCRIPT -> Known.TYPESCRIPT + PYTHON -> Known.PYTHON + PLAYGROUND -> Known.PLAYGROUND + else -> throw StagehandInvalidDataException("Unknown XLanguage: $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 + + fun validate(): XLanguage = 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 XLanguage && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Whether to stream the response via SSE */ class XStreamResponse @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -887,7 +1090,10 @@ private constructor( } return other is SessionObserveParams && - sessionId == other.sessionId && + id == other.id && + xLanguage == other.xLanguage && + xSdkVersion == other.xSdkVersion && + xSentAt == other.xSentAt && xStreamResponse == other.xStreamResponse && body == other.body && additionalHeaders == other.additionalHeaders && @@ -895,8 +1101,17 @@ private constructor( } override fun hashCode(): Int = - Objects.hash(sessionId, xStreamResponse, body, additionalHeaders, additionalQueryParams) + Objects.hash( + id, + xLanguage, + xSdkVersion, + xSentAt, + xStreamResponse, + body, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "SessionObserveParams{sessionId=$sessionId, xStreamResponse=$xStreamResponse, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "SessionObserveParams{id=$id, xLanguage=$xLanguage, xSdkVersion=$xSdkVersion, xSentAt=$xSentAt, xStreamResponse=$xStreamResponse, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveResponse.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveResponse.kt new file mode 100644 index 0000000..cb2404e --- /dev/null +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveResponse.kt @@ -0,0 +1,420 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.browserbase.api.models.sessions + +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.checkKnown +import com.browserbase.api.core.checkRequired +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 java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class SessionObserveResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val data: JsonField, + private val success: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(), + @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(), + ) : this(data, success, 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 data(): Data = data.getRequired("data") + + /** + * Indicates whether the request was successful + * + * @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 success(): Boolean = success.getRequired("success") + + /** + * Returns the raw JSON value of [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField = data + + /** + * Returns the raw JSON value of [success]. + * + * Unlike [success], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success + + @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 [SessionObserveResponse]. + * + * The following fields are required: + * ```java + * .data() + * .success() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [SessionObserveResponse]. */ + class Builder internal constructor() { + + private var data: JsonField? = null + private var success: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(sessionObserveResponse: SessionObserveResponse) = apply { + data = sessionObserveResponse.data + success = sessionObserveResponse.success + additionalProperties = sessionObserveResponse.additionalProperties.toMutableMap() + } + + fun data(data: Data) = data(JsonField.of(data)) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed [Data] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun data(data: JsonField) = apply { this.data = data } + + /** Indicates whether the request was successful */ + fun success(success: Boolean) = success(JsonField.of(success)) + + /** + * Sets [Builder.success] to an arbitrary JSON value. + * + * You should usually call [Builder.success] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun success(success: JsonField) = apply { this.success = success } + + 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 [SessionObserveResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .data() + * .success() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): SessionObserveResponse = + SessionObserveResponse( + checkRequired("data", data), + checkRequired("success", success), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): SessionObserveResponse = apply { + if (validated) { + return@apply + } + + data().validate() + success() + 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + (if (success.asKnown().isPresent) 1 else 0) + + class Data + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val result: JsonField>, + private val actionId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("result") + @ExcludeMissing + result: JsonField> = JsonMissing.of(), + @JsonProperty("actionId") @ExcludeMissing actionId: JsonField = JsonMissing.of(), + ) : this(result, actionId, 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 result(): List = result.getRequired("result") + + /** + * Action ID for tracking + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun actionId(): Optional = actionId.getOptional("actionId") + + /** + * Returns the raw JSON value of [result]. + * + * Unlike [result], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("result") @ExcludeMissing fun _result(): JsonField> = result + + /** + * Returns the raw JSON value of [actionId]. + * + * Unlike [actionId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("actionId") @ExcludeMissing fun _actionId(): JsonField = actionId + + @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 [Data]. + * + * The following fields are required: + * ```java + * .result() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Data]. */ + class Builder internal constructor() { + + private var result: JsonField>? = null + private var actionId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(data: Data) = apply { + result = data.result.map { it.toMutableList() } + actionId = data.actionId + additionalProperties = data.additionalProperties.toMutableMap() + } + + fun result(result: List) = result(JsonField.of(result)) + + /** + * Sets [Builder.result] to an arbitrary JSON value. + * + * You should usually call [Builder.result] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun result(result: JsonField>) = apply { + this.result = result.map { it.toMutableList() } + } + + /** + * Adds a single [Action] to [Builder.result]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addResult(result: Action) = apply { + this.result = + (this.result ?: JsonField.of(mutableListOf())).also { + checkKnown("result", it).add(result) + } + } + + /** Action ID for tracking */ + fun actionId(actionId: String) = actionId(JsonField.of(actionId)) + + /** + * Sets [Builder.actionId] to an arbitrary JSON value. + * + * You should usually call [Builder.actionId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun actionId(actionId: JsonField) = apply { this.actionId = actionId } + + 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 [Data]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .result() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Data = + Data( + checkRequired("result", result).map { it.toImmutable() }, + actionId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Data = apply { + if (validated) { + return@apply + } + + result().forEach { it.validate() } + actionId() + 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 = + (result.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (actionId.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Data && + result == other.result && + actionId == other.actionId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(result, actionId, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Data{result=$result, actionId=$actionId, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SessionObserveResponse && + data == other.data && + success == other.success && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(data, success, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "SessionObserveResponse{data=$data, success=$success, additionalProperties=$additionalProperties}" +} diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartParams.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartParams.kt index cda3e99..16eecb4 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartParams.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartParams.kt @@ -2,65 +2,123 @@ 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.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 +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.time.OffsetDateTime +import java.time.format.DateTimeFormatter import java.util.Collections import java.util.Objects import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** - * Initializes a new Stagehand session with a browser instance. Returns a session ID that must be - * used for all subsequent requests. + * Creates a new browser session with the specified configuration. Returns a session ID used for all + * subsequent operations. */ class SessionStartParams private constructor( + private val xLanguage: XLanguage?, + private val xSdkVersion: String?, + private val xSentAt: OffsetDateTime?, + private val xStreamResponse: XStreamResponse?, private val body: Body, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, ) : Params { + /** Client SDK language */ + fun xLanguage(): Optional = Optional.ofNullable(xLanguage) + + /** Version of the Stagehand SDK */ + fun xSdkVersion(): Optional = Optional.ofNullable(xSdkVersion) + + /** ISO timestamp when request was sent */ + fun xSentAt(): Optional = Optional.ofNullable(xSentAt) + + /** Whether to stream the response via SSE */ + fun xStreamResponse(): Optional = Optional.ofNullable(xStreamResponse) + /** - * API key for Browserbase Cloud + * Model name to use for AI operations * * @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 browserbaseApiKey(): String = body.browserbaseApiKey() + fun modelName(): String = body.modelName() /** - * Project ID for Browserbase + * Timeout in ms for act operations * - * @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). + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). */ - fun browserbaseProjectId(): String = body.browserbaseProjectId() + fun actTimeoutMs(): Optional = body.actTimeoutMs() /** - * Timeout in ms to wait for DOM to settle + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun browser(): Optional = body.browser() + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun browserbaseSessionCreateParams(): Optional = + body.browserbaseSessionCreateParams() + + /** + * Existing Browserbase session ID to resume * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ - fun domSettleTimeout(): Optional = body.domSettleTimeout() + fun browserbaseSessionId(): Optional = body.browserbaseSessionId() + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun debugDom(): Optional = body.debugDom() /** - * AI model to use for actions (must be prefixed with provider/) + * Timeout in ms to wait for DOM to settle * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ - fun model(): Optional = body.model() + fun domSettleTimeoutMs(): Optional = body.domSettleTimeoutMs() + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun experimental(): Optional = body.experimental() /** * Enable self-healing for failed actions @@ -71,7 +129,7 @@ private constructor( fun selfHeal(): Optional = body.selfHeal() /** - * Custom system prompt for AI actions + * Custom system prompt for AI operations * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -79,7 +137,7 @@ private constructor( fun systemPrompt(): Optional = body.systemPrompt() /** - * Logging verbosity level + * Logging verbosity level (0=quiet, 1=normal, 2=debug) * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -87,35 +145,70 @@ private constructor( fun verbose(): Optional = body.verbose() /** - * Returns the raw JSON value of [browserbaseApiKey]. + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun waitForCaptchaSolves(): Optional = body.waitForCaptchaSolves() + + /** + * Returns the raw JSON value of [modelName]. * - * Unlike [browserbaseApiKey], this method doesn't throw if the JSON field has an unexpected - * type. + * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _modelName(): JsonField = body._modelName() + + /** + * Returns the raw JSON value of [actTimeoutMs]. + * + * Unlike [actTimeoutMs], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _actTimeoutMs(): JsonField = body._actTimeoutMs() + + /** + * Returns the raw JSON value of [browser]. + * + * Unlike [browser], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _browser(): JsonField = body._browser() + + /** + * Returns the raw JSON value of [browserbaseSessionCreateParams]. + * + * Unlike [browserbaseSessionCreateParams], this method doesn't throw if the JSON field has an + * unexpected type. */ - fun _browserbaseApiKey(): JsonField = body._browserbaseApiKey() + fun _browserbaseSessionCreateParams(): JsonField = + body._browserbaseSessionCreateParams() /** - * Returns the raw JSON value of [browserbaseProjectId]. + * Returns the raw JSON value of [browserbaseSessionId]. * - * Unlike [browserbaseProjectId], this method doesn't throw if the JSON field has an unexpected + * Unlike [browserbaseSessionId], this method doesn't throw if the JSON field has an unexpected * type. */ - fun _browserbaseProjectId(): JsonField = body._browserbaseProjectId() + fun _browserbaseSessionId(): JsonField = body._browserbaseSessionId() /** - * Returns the raw JSON value of [domSettleTimeout]. + * Returns the raw JSON value of [debugDom]. * - * Unlike [domSettleTimeout], this method doesn't throw if the JSON field has an unexpected + * Unlike [debugDom], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _debugDom(): JsonField = body._debugDom() + + /** + * Returns the raw JSON value of [domSettleTimeoutMs]. + * + * Unlike [domSettleTimeoutMs], this method doesn't throw if the JSON field has an unexpected * type. */ - fun _domSettleTimeout(): JsonField = body._domSettleTimeout() + fun _domSettleTimeoutMs(): JsonField = body._domSettleTimeoutMs() /** - * Returns the raw JSON value of [model]. + * Returns the raw JSON value of [experimental]. * - * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [experimental], this method doesn't throw if the JSON field has an unexpected type. */ - fun _model(): JsonField = body._model() + fun _experimental(): JsonField = body._experimental() /** * Returns the raw JSON value of [selfHeal]. @@ -138,6 +231,14 @@ private constructor( */ fun _verbose(): JsonField = body._verbose() + /** + * Returns the raw JSON value of [waitForCaptchaSolves]. + * + * Unlike [waitForCaptchaSolves], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _waitForCaptchaSolves(): JsonField = body._waitForCaptchaSolves() + fun _additionalBodyProperties(): Map = body._additionalProperties() /** Additional headers to send with the request. */ @@ -155,8 +256,7 @@ private constructor( * * The following fields are required: * ```java - * .browserbaseApiKey() - * .browserbaseProjectId() + * .modelName() * ``` */ @JvmStatic fun builder() = Builder() @@ -165,89 +265,172 @@ private constructor( /** A builder for [SessionStartParams]. */ class Builder internal constructor() { + private var xLanguage: XLanguage? = null + private var xSdkVersion: String? = null + private var xSentAt: OffsetDateTime? = null + private var xStreamResponse: XStreamResponse? = null private var body: Body.Builder = Body.builder() private var additionalHeaders: Headers.Builder = Headers.builder() private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() @JvmSynthetic internal fun from(sessionStartParams: SessionStartParams) = apply { + xLanguage = sessionStartParams.xLanguage + xSdkVersion = sessionStartParams.xSdkVersion + xSentAt = sessionStartParams.xSentAt + xStreamResponse = sessionStartParams.xStreamResponse body = sessionStartParams.body.toBuilder() additionalHeaders = sessionStartParams.additionalHeaders.toBuilder() additionalQueryParams = sessionStartParams.additionalQueryParams.toBuilder() } + /** Client SDK language */ + fun xLanguage(xLanguage: XLanguage?) = apply { this.xLanguage = xLanguage } + + /** Alias for calling [Builder.xLanguage] with `xLanguage.orElse(null)`. */ + fun xLanguage(xLanguage: Optional) = xLanguage(xLanguage.getOrNull()) + + /** Version of the Stagehand SDK */ + fun xSdkVersion(xSdkVersion: String?) = apply { this.xSdkVersion = xSdkVersion } + + /** Alias for calling [Builder.xSdkVersion] with `xSdkVersion.orElse(null)`. */ + fun xSdkVersion(xSdkVersion: Optional) = xSdkVersion(xSdkVersion.getOrNull()) + + /** ISO timestamp when request was sent */ + fun xSentAt(xSentAt: OffsetDateTime?) = apply { this.xSentAt = xSentAt } + + /** Alias for calling [Builder.xSentAt] with `xSentAt.orElse(null)`. */ + fun xSentAt(xSentAt: Optional) = xSentAt(xSentAt.getOrNull()) + + /** Whether to stream the response via SSE */ + fun xStreamResponse(xStreamResponse: XStreamResponse?) = apply { + this.xStreamResponse = xStreamResponse + } + + /** Alias for calling [Builder.xStreamResponse] with `xStreamResponse.orElse(null)`. */ + fun xStreamResponse(xStreamResponse: Optional) = + xStreamResponse(xStreamResponse.getOrNull()) + /** * Sets the entire request body. * * This is generally only useful if you are already constructing the body separately. * Otherwise, it's more convenient to use the top-level setters instead: - * - [browserbaseApiKey] - * - [browserbaseProjectId] - * - [domSettleTimeout] - * - [model] - * - [selfHeal] + * - [modelName] + * - [actTimeoutMs] + * - [browser] + * - [browserbaseSessionCreateParams] + * - [browserbaseSessionId] * - etc. */ fun body(body: Body) = apply { this.body = body.toBuilder() } - /** API key for Browserbase Cloud */ - fun browserbaseApiKey(browserbaseApiKey: String) = apply { - body.browserbaseApiKey(browserbaseApiKey) - } + /** Model name to use for AI operations */ + fun modelName(modelName: String) = apply { body.modelName(modelName) } /** - * Sets [Builder.browserbaseApiKey] to an arbitrary JSON value. + * Sets [Builder.modelName] to an arbitrary JSON value. * - * You should usually call [Builder.browserbaseApiKey] with a well-typed [String] value - * instead. This method is primarily for setting the field to an undocumented or not yet - * supported 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 { body.modelName(modelName) } + + /** Timeout in ms for act operations */ + fun actTimeoutMs(actTimeoutMs: Double) = apply { body.actTimeoutMs(actTimeoutMs) } + + /** + * Sets [Builder.actTimeoutMs] to an arbitrary JSON value. + * + * You should usually call [Builder.actTimeoutMs] with a well-typed [Double] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun browserbaseApiKey(browserbaseApiKey: JsonField) = apply { - body.browserbaseApiKey(browserbaseApiKey) + fun actTimeoutMs(actTimeoutMs: JsonField) = apply { + body.actTimeoutMs(actTimeoutMs) } - /** Project ID for Browserbase */ - fun browserbaseProjectId(browserbaseProjectId: String) = apply { - body.browserbaseProjectId(browserbaseProjectId) + fun browser(browser: Browser) = apply { body.browser(browser) } + + /** + * Sets [Builder.browser] to an arbitrary JSON value. + * + * You should usually call [Builder.browser] with a well-typed [Browser] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun browser(browser: JsonField) = apply { body.browser(browser) } + + fun browserbaseSessionCreateParams( + browserbaseSessionCreateParams: BrowserbaseSessionCreateParams + ) = apply { body.browserbaseSessionCreateParams(browserbaseSessionCreateParams) } + + /** + * Sets [Builder.browserbaseSessionCreateParams] to an arbitrary JSON value. + * + * You should usually call [Builder.browserbaseSessionCreateParams] with a well-typed + * [BrowserbaseSessionCreateParams] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun browserbaseSessionCreateParams( + browserbaseSessionCreateParams: JsonField + ) = apply { body.browserbaseSessionCreateParams(browserbaseSessionCreateParams) } + + /** Existing Browserbase session ID to resume */ + fun browserbaseSessionId(browserbaseSessionId: String) = apply { + body.browserbaseSessionId(browserbaseSessionId) } /** - * Sets [Builder.browserbaseProjectId] to an arbitrary JSON value. + * Sets [Builder.browserbaseSessionId] to an arbitrary JSON value. * - * You should usually call [Builder.browserbaseProjectId] with a well-typed [String] value + * You should usually call [Builder.browserbaseSessionId] with a well-typed [String] value * instead. This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun browserbaseProjectId(browserbaseProjectId: JsonField) = apply { - body.browserbaseProjectId(browserbaseProjectId) + fun browserbaseSessionId(browserbaseSessionId: JsonField) = apply { + body.browserbaseSessionId(browserbaseSessionId) } + fun debugDom(debugDom: Boolean) = apply { body.debugDom(debugDom) } + + /** + * Sets [Builder.debugDom] to an arbitrary JSON value. + * + * You should usually call [Builder.debugDom] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun debugDom(debugDom: JsonField) = apply { body.debugDom(debugDom) } + /** Timeout in ms to wait for DOM to settle */ - fun domSettleTimeout(domSettleTimeout: Long) = apply { - body.domSettleTimeout(domSettleTimeout) + fun domSettleTimeoutMs(domSettleTimeoutMs: Double) = apply { + body.domSettleTimeoutMs(domSettleTimeoutMs) } /** - * Sets [Builder.domSettleTimeout] to an arbitrary JSON value. + * Sets [Builder.domSettleTimeoutMs] to an arbitrary JSON value. * - * You should usually call [Builder.domSettleTimeout] with a well-typed [Long] value + * You should usually call [Builder.domSettleTimeoutMs] with a well-typed [Double] value * instead. This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun domSettleTimeout(domSettleTimeout: JsonField) = apply { - body.domSettleTimeout(domSettleTimeout) + fun domSettleTimeoutMs(domSettleTimeoutMs: JsonField) = apply { + body.domSettleTimeoutMs(domSettleTimeoutMs) } - /** AI model to use for actions (must be prefixed with provider/) */ - fun model(model: String) = apply { body.model(model) } + fun experimental(experimental: Boolean) = apply { body.experimental(experimental) } /** - * Sets [Builder.model] to an arbitrary JSON value. + * Sets [Builder.experimental] to an arbitrary JSON value. * - * You should usually call [Builder.model] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. + * You should usually call [Builder.experimental] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. */ - fun model(model: JsonField) = apply { body.model(model) } + fun experimental(experimental: JsonField) = apply { + body.experimental(experimental) + } /** Enable self-healing for failed actions */ fun selfHeal(selfHeal: Boolean) = apply { body.selfHeal(selfHeal) } @@ -261,7 +444,7 @@ private constructor( */ fun selfHeal(selfHeal: JsonField) = apply { body.selfHeal(selfHeal) } - /** Custom system prompt for AI actions */ + /** Custom system prompt for AI operations */ fun systemPrompt(systemPrompt: String) = apply { body.systemPrompt(systemPrompt) } /** @@ -275,7 +458,7 @@ private constructor( body.systemPrompt(systemPrompt) } - /** Logging verbosity level */ + /** Logging verbosity level (0=quiet, 1=normal, 2=debug) */ fun verbose(verbose: Long) = apply { body.verbose(verbose) } /** @@ -286,6 +469,21 @@ private constructor( */ fun verbose(verbose: JsonField) = apply { body.verbose(verbose) } + fun waitForCaptchaSolves(waitForCaptchaSolves: Boolean) = apply { + body.waitForCaptchaSolves(waitForCaptchaSolves) + } + + /** + * Sets [Builder.waitForCaptchaSolves] to an arbitrary JSON value. + * + * You should usually call [Builder.waitForCaptchaSolves] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun waitForCaptchaSolves(waitForCaptchaSolves: JsonField) = apply { + body.waitForCaptchaSolves(waitForCaptchaSolves) + } + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { body.additionalProperties(additionalBodyProperties) } @@ -410,14 +608,17 @@ private constructor( * * The following fields are required: * ```java - * .browserbaseApiKey() - * .browserbaseProjectId() + * .modelName() * ``` * * @throws IllegalStateException if any required field is unset. */ fun build(): SessionStartParams = SessionStartParams( + xLanguage, + xSdkVersion, + xSentAt, + xStreamResponse, body.build(), additionalHeaders.build(), additionalQueryParams.build(), @@ -426,35 +627,62 @@ private constructor( fun _body(): Body = body - override fun _headers(): Headers = additionalHeaders + override fun _headers(): Headers = + Headers.builder() + .apply { + xLanguage?.let { put("x-language", it.toString()) } + xSdkVersion?.let { put("x-sdk-version", it) } + xSentAt?.let { put("x-sent-at", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) } + xStreamResponse?.let { put("x-stream-response", it.toString()) } + putAll(additionalHeaders) + } + .build() override fun _queryParams(): QueryParams = additionalQueryParams class Body @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val browserbaseApiKey: JsonField, - private val browserbaseProjectId: JsonField, - private val domSettleTimeout: JsonField, - private val model: JsonField, + private val modelName: JsonField, + private val actTimeoutMs: JsonField, + private val browser: JsonField, + private val browserbaseSessionCreateParams: JsonField, + private val browserbaseSessionId: JsonField, + private val debugDom: JsonField, + private val domSettleTimeoutMs: JsonField, + private val experimental: JsonField, private val selfHeal: JsonField, private val systemPrompt: JsonField, private val verbose: JsonField, + private val waitForCaptchaSolves: JsonField, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("BROWSERBASE_API_KEY") + @JsonProperty("modelName") + @ExcludeMissing + modelName: JsonField = JsonMissing.of(), + @JsonProperty("actTimeoutMs") + @ExcludeMissing + actTimeoutMs: JsonField = JsonMissing.of(), + @JsonProperty("browser") @ExcludeMissing browser: JsonField = JsonMissing.of(), + @JsonProperty("browserbaseSessionCreateParams") @ExcludeMissing - browserbaseApiKey: JsonField = JsonMissing.of(), - @JsonProperty("BROWSERBASE_PROJECT_ID") + browserbaseSessionCreateParams: JsonField = + JsonMissing.of(), + @JsonProperty("browserbaseSessionID") @ExcludeMissing - browserbaseProjectId: JsonField = JsonMissing.of(), - @JsonProperty("domSettleTimeout") + browserbaseSessionId: JsonField = JsonMissing.of(), + @JsonProperty("debugDom") @ExcludeMissing - domSettleTimeout: JsonField = JsonMissing.of(), - @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + debugDom: JsonField = JsonMissing.of(), + @JsonProperty("domSettleTimeoutMs") + @ExcludeMissing + domSettleTimeoutMs: JsonField = JsonMissing.of(), + @JsonProperty("experimental") + @ExcludeMissing + experimental: JsonField = JsonMissing.of(), @JsonProperty("selfHeal") @ExcludeMissing selfHeal: JsonField = JsonMissing.of(), @@ -462,49 +690,83 @@ private constructor( @ExcludeMissing systemPrompt: JsonField = JsonMissing.of(), @JsonProperty("verbose") @ExcludeMissing verbose: JsonField = JsonMissing.of(), + @JsonProperty("waitForCaptchaSolves") + @ExcludeMissing + waitForCaptchaSolves: JsonField = JsonMissing.of(), ) : this( - browserbaseApiKey, - browserbaseProjectId, - domSettleTimeout, - model, + modelName, + actTimeoutMs, + browser, + browserbaseSessionCreateParams, + browserbaseSessionId, + debugDom, + domSettleTimeoutMs, + experimental, selfHeal, systemPrompt, verbose, + waitForCaptchaSolves, mutableMapOf(), ) /** - * API key for Browserbase Cloud + * Model name to use for AI operations * * @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 browserbaseApiKey(): String = browserbaseApiKey.getRequired("BROWSERBASE_API_KEY") + fun modelName(): String = modelName.getRequired("modelName") /** - * Project ID for Browserbase + * Timeout in ms for act operations * - * @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). + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). */ - fun browserbaseProjectId(): String = - browserbaseProjectId.getRequired("BROWSERBASE_PROJECT_ID") + fun actTimeoutMs(): Optional = actTimeoutMs.getOptional("actTimeoutMs") /** - * Timeout in ms to wait for DOM to settle + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun browser(): Optional = browser.getOptional("browser") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun browserbaseSessionCreateParams(): Optional = + browserbaseSessionCreateParams.getOptional("browserbaseSessionCreateParams") + + /** + * Existing Browserbase session ID to resume * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun domSettleTimeout(): Optional = domSettleTimeout.getOptional("domSettleTimeout") + fun browserbaseSessionId(): Optional = + browserbaseSessionId.getOptional("browserbaseSessionID") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun debugDom(): Optional = debugDom.getOptional("debugDom") /** - * AI model to use for actions (must be prefixed with provider/) + * Timeout in ms to wait for DOM to settle * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ - fun model(): Optional = model.getOptional("model") + fun domSettleTimeoutMs(): Optional = + domSettleTimeoutMs.getOptional("domSettleTimeoutMs") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun experimental(): Optional = experimental.getOptional("experimental") /** * Enable self-healing for failed actions @@ -515,7 +777,7 @@ private constructor( fun selfHeal(): Optional = selfHeal.getOptional("selfHeal") /** - * Custom system prompt for AI actions + * Custom system prompt for AI operations * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). @@ -523,7 +785,7 @@ private constructor( fun systemPrompt(): Optional = systemPrompt.getOptional("systemPrompt") /** - * Logging verbosity level + * Logging verbosity level (0=quiet, 1=normal, 2=debug) * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). @@ -531,41 +793,83 @@ private constructor( fun verbose(): Optional = verbose.getOptional("verbose") /** - * Returns the raw JSON value of [browserbaseApiKey]. + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun waitForCaptchaSolves(): Optional = + waitForCaptchaSolves.getOptional("waitForCaptchaSolves") + + /** + * 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 [actTimeoutMs]. * - * Unlike [browserbaseApiKey], this method doesn't throw if the JSON field has an unexpected + * Unlike [actTimeoutMs], this method doesn't throw if the JSON field has an unexpected * type. */ - @JsonProperty("BROWSERBASE_API_KEY") + @JsonProperty("actTimeoutMs") + @ExcludeMissing + fun _actTimeoutMs(): JsonField = actTimeoutMs + + /** + * Returns the raw JSON value of [browser]. + * + * Unlike [browser], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("browser") @ExcludeMissing fun _browser(): JsonField = browser + + /** + * Returns the raw JSON value of [browserbaseSessionCreateParams]. + * + * Unlike [browserbaseSessionCreateParams], this method doesn't throw if the JSON field has + * an unexpected type. + */ + @JsonProperty("browserbaseSessionCreateParams") @ExcludeMissing - fun _browserbaseApiKey(): JsonField = browserbaseApiKey + fun _browserbaseSessionCreateParams(): JsonField = + browserbaseSessionCreateParams /** - * Returns the raw JSON value of [browserbaseProjectId]. + * Returns the raw JSON value of [browserbaseSessionId]. * - * Unlike [browserbaseProjectId], this method doesn't throw if the JSON field has an + * Unlike [browserbaseSessionId], this method doesn't throw if the JSON field has an * unexpected type. */ - @JsonProperty("BROWSERBASE_PROJECT_ID") + @JsonProperty("browserbaseSessionID") @ExcludeMissing - fun _browserbaseProjectId(): JsonField = browserbaseProjectId + fun _browserbaseSessionId(): JsonField = browserbaseSessionId /** - * Returns the raw JSON value of [domSettleTimeout]. + * Returns the raw JSON value of [debugDom]. * - * Unlike [domSettleTimeout], this method doesn't throw if the JSON field has an unexpected - * type. + * Unlike [debugDom], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("domSettleTimeout") + @JsonProperty("debugDom") @ExcludeMissing fun _debugDom(): JsonField = debugDom + + /** + * Returns the raw JSON value of [domSettleTimeoutMs]. + * + * Unlike [domSettleTimeoutMs], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("domSettleTimeoutMs") @ExcludeMissing - fun _domSettleTimeout(): JsonField = domSettleTimeout + fun _domSettleTimeoutMs(): JsonField = domSettleTimeoutMs /** - * Returns the raw JSON value of [model]. + * Returns the raw JSON value of [experimental]. * - * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [experimental], this method doesn't throw if the JSON field has an unexpected + * type. */ - @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + @JsonProperty("experimental") + @ExcludeMissing + fun _experimental(): JsonField = experimental /** * Returns the raw JSON value of [selfHeal]. @@ -591,6 +895,16 @@ private constructor( */ @JsonProperty("verbose") @ExcludeMissing fun _verbose(): JsonField = verbose + /** + * Returns the raw JSON value of [waitForCaptchaSolves]. + * + * Unlike [waitForCaptchaSolves], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("waitForCaptchaSolves") + @ExcludeMissing + fun _waitForCaptchaSolves(): JsonField = waitForCaptchaSolves + @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { additionalProperties.put(key, value) @@ -610,8 +924,7 @@ private constructor( * * The following fields are required: * ```java - * .browserbaseApiKey() - * .browserbaseProjectId() + * .modelName() * ``` */ @JvmStatic fun builder() = Builder() @@ -620,83 +933,143 @@ private constructor( /** A builder for [Body]. */ class Builder internal constructor() { - private var browserbaseApiKey: JsonField? = null - private var browserbaseProjectId: JsonField? = null - private var domSettleTimeout: JsonField = JsonMissing.of() - private var model: JsonField = JsonMissing.of() + private var modelName: JsonField? = null + private var actTimeoutMs: JsonField = JsonMissing.of() + private var browser: JsonField = JsonMissing.of() + private var browserbaseSessionCreateParams: JsonField = + JsonMissing.of() + private var browserbaseSessionId: JsonField = JsonMissing.of() + private var debugDom: JsonField = JsonMissing.of() + private var domSettleTimeoutMs: JsonField = JsonMissing.of() + private var experimental: JsonField = JsonMissing.of() private var selfHeal: JsonField = JsonMissing.of() private var systemPrompt: JsonField = JsonMissing.of() private var verbose: JsonField = JsonMissing.of() + private var waitForCaptchaSolves: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(body: Body) = apply { - browserbaseApiKey = body.browserbaseApiKey - browserbaseProjectId = body.browserbaseProjectId - domSettleTimeout = body.domSettleTimeout - model = body.model + modelName = body.modelName + actTimeoutMs = body.actTimeoutMs + browser = body.browser + browserbaseSessionCreateParams = body.browserbaseSessionCreateParams + browserbaseSessionId = body.browserbaseSessionId + debugDom = body.debugDom + domSettleTimeoutMs = body.domSettleTimeoutMs + experimental = body.experimental selfHeal = body.selfHeal systemPrompt = body.systemPrompt verbose = body.verbose + waitForCaptchaSolves = body.waitForCaptchaSolves additionalProperties = body.additionalProperties.toMutableMap() } - /** API key for Browserbase Cloud */ - fun browserbaseApiKey(browserbaseApiKey: String) = - browserbaseApiKey(JsonField.of(browserbaseApiKey)) + /** Model name to use for AI operations */ + 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 } + + /** Timeout in ms for act operations */ + fun actTimeoutMs(actTimeoutMs: Double) = actTimeoutMs(JsonField.of(actTimeoutMs)) /** - * Sets [Builder.browserbaseApiKey] to an arbitrary JSON value. + * Sets [Builder.actTimeoutMs] to an arbitrary JSON value. * - * You should usually call [Builder.browserbaseApiKey] with a well-typed [String] value + * You should usually call [Builder.actTimeoutMs] with a well-typed [Double] value * instead. This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun browserbaseApiKey(browserbaseApiKey: JsonField) = apply { - this.browserbaseApiKey = browserbaseApiKey + fun actTimeoutMs(actTimeoutMs: JsonField) = apply { + this.actTimeoutMs = actTimeoutMs } - /** Project ID for Browserbase */ - fun browserbaseProjectId(browserbaseProjectId: String) = - browserbaseProjectId(JsonField.of(browserbaseProjectId)) + fun browser(browser: Browser) = browser(JsonField.of(browser)) + + /** + * Sets [Builder.browser] to an arbitrary JSON value. + * + * You should usually call [Builder.browser] with a well-typed [Browser] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun browser(browser: JsonField) = apply { this.browser = browser } + + fun browserbaseSessionCreateParams( + browserbaseSessionCreateParams: BrowserbaseSessionCreateParams + ) = browserbaseSessionCreateParams(JsonField.of(browserbaseSessionCreateParams)) + + /** + * Sets [Builder.browserbaseSessionCreateParams] to an arbitrary JSON value. + * + * You should usually call [Builder.browserbaseSessionCreateParams] with a well-typed + * [BrowserbaseSessionCreateParams] value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun browserbaseSessionCreateParams( + browserbaseSessionCreateParams: JsonField + ) = apply { this.browserbaseSessionCreateParams = browserbaseSessionCreateParams } + + /** Existing Browserbase session ID to resume */ + fun browserbaseSessionId(browserbaseSessionId: String) = + browserbaseSessionId(JsonField.of(browserbaseSessionId)) /** - * Sets [Builder.browserbaseProjectId] to an arbitrary JSON value. + * Sets [Builder.browserbaseSessionId] to an arbitrary JSON value. * - * You should usually call [Builder.browserbaseProjectId] with a well-typed [String] + * You should usually call [Builder.browserbaseSessionId] with a well-typed [String] * value instead. This method is primarily for setting the field to an undocumented or * not yet supported value. */ - fun browserbaseProjectId(browserbaseProjectId: JsonField) = apply { - this.browserbaseProjectId = browserbaseProjectId + fun browserbaseSessionId(browserbaseSessionId: JsonField) = apply { + this.browserbaseSessionId = browserbaseSessionId } + fun debugDom(debugDom: Boolean) = debugDom(JsonField.of(debugDom)) + + /** + * Sets [Builder.debugDom] to an arbitrary JSON value. + * + * You should usually call [Builder.debugDom] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun debugDom(debugDom: JsonField) = apply { this.debugDom = debugDom } + /** Timeout in ms to wait for DOM to settle */ - fun domSettleTimeout(domSettleTimeout: Long) = - domSettleTimeout(JsonField.of(domSettleTimeout)) + fun domSettleTimeoutMs(domSettleTimeoutMs: Double) = + domSettleTimeoutMs(JsonField.of(domSettleTimeoutMs)) /** - * Sets [Builder.domSettleTimeout] to an arbitrary JSON value. + * Sets [Builder.domSettleTimeoutMs] to an arbitrary JSON value. * - * You should usually call [Builder.domSettleTimeout] with a well-typed [Long] value + * You should usually call [Builder.domSettleTimeoutMs] with a well-typed [Double] value * instead. This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun domSettleTimeout(domSettleTimeout: JsonField) = apply { - this.domSettleTimeout = domSettleTimeout + fun domSettleTimeoutMs(domSettleTimeoutMs: JsonField) = apply { + this.domSettleTimeoutMs = domSettleTimeoutMs } - /** AI model to use for actions (must be prefixed with provider/) */ - fun model(model: String) = model(JsonField.of(model)) + fun experimental(experimental: Boolean) = experimental(JsonField.of(experimental)) /** - * Sets [Builder.model] to an arbitrary JSON value. + * Sets [Builder.experimental] to an arbitrary JSON value. * - * You should usually call [Builder.model] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet + * You should usually call [Builder.experimental] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun model(model: JsonField) = apply { this.model = model } + fun experimental(experimental: JsonField) = apply { + this.experimental = experimental + } /** Enable self-healing for failed actions */ fun selfHeal(selfHeal: Boolean) = selfHeal(JsonField.of(selfHeal)) @@ -710,7 +1083,7 @@ private constructor( */ fun selfHeal(selfHeal: JsonField) = apply { this.selfHeal = selfHeal } - /** Custom system prompt for AI actions */ + /** Custom system prompt for AI operations */ fun systemPrompt(systemPrompt: String) = systemPrompt(JsonField.of(systemPrompt)) /** @@ -724,7 +1097,7 @@ private constructor( this.systemPrompt = systemPrompt } - /** Logging verbosity level */ + /** Logging verbosity level (0=quiet, 1=normal, 2=debug) */ fun verbose(verbose: Long) = verbose(JsonField.of(verbose)) /** @@ -736,6 +1109,20 @@ private constructor( */ fun verbose(verbose: JsonField) = apply { this.verbose = verbose } + fun waitForCaptchaSolves(waitForCaptchaSolves: Boolean) = + waitForCaptchaSolves(JsonField.of(waitForCaptchaSolves)) + + /** + * Sets [Builder.waitForCaptchaSolves] to an arbitrary JSON value. + * + * You should usually call [Builder.waitForCaptchaSolves] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun waitForCaptchaSolves(waitForCaptchaSolves: JsonField) = apply { + this.waitForCaptchaSolves = waitForCaptchaSolves + } + fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() putAllAdditionalProperties(additionalProperties) @@ -762,21 +1149,25 @@ private constructor( * * The following fields are required: * ```java - * .browserbaseApiKey() - * .browserbaseProjectId() + * .modelName() * ``` * * @throws IllegalStateException if any required field is unset. */ fun build(): Body = Body( - checkRequired("browserbaseApiKey", browserbaseApiKey), - checkRequired("browserbaseProjectId", browserbaseProjectId), - domSettleTimeout, - model, + checkRequired("modelName", modelName), + actTimeoutMs, + browser, + browserbaseSessionCreateParams, + browserbaseSessionId, + debugDom, + domSettleTimeoutMs, + experimental, selfHeal, systemPrompt, verbose, + waitForCaptchaSolves, additionalProperties.toMutableMap(), ) } @@ -788,13 +1179,18 @@ private constructor( return@apply } - browserbaseApiKey() - browserbaseProjectId() - domSettleTimeout() - model() + modelName() + actTimeoutMs() + browser().ifPresent { it.validate() } + browserbaseSessionCreateParams().ifPresent { it.validate() } + browserbaseSessionId() + debugDom() + domSettleTimeoutMs() + experimental() selfHeal() systemPrompt() verbose() + waitForCaptchaSolves() validated = true } @@ -814,13 +1210,18 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (if (browserbaseApiKey.asKnown().isPresent) 1 else 0) + - (if (browserbaseProjectId.asKnown().isPresent) 1 else 0) + - (if (domSettleTimeout.asKnown().isPresent) 1 else 0) + - (if (model.asKnown().isPresent) 1 else 0) + + (if (modelName.asKnown().isPresent) 1 else 0) + + (if (actTimeoutMs.asKnown().isPresent) 1 else 0) + + (browser.asKnown().getOrNull()?.validity() ?: 0) + + (browserbaseSessionCreateParams.asKnown().getOrNull()?.validity() ?: 0) + + (if (browserbaseSessionId.asKnown().isPresent) 1 else 0) + + (if (debugDom.asKnown().isPresent) 1 else 0) + + (if (domSettleTimeoutMs.asKnown().isPresent) 1 else 0) + + (if (experimental.asKnown().isPresent) 1 else 0) + (if (selfHeal.asKnown().isPresent) 1 else 0) + (if (systemPrompt.asKnown().isPresent) 1 else 0) + - (if (verbose.asKnown().isPresent) 1 else 0) + (if (verbose.asKnown().isPresent) 1 else 0) + + (if (waitForCaptchaSolves.asKnown().isPresent) 1 else 0) override fun equals(other: Any?): Boolean { if (this === other) { @@ -828,25 +1229,35 @@ private constructor( } return other is Body && - browserbaseApiKey == other.browserbaseApiKey && - browserbaseProjectId == other.browserbaseProjectId && - domSettleTimeout == other.domSettleTimeout && - model == other.model && + modelName == other.modelName && + actTimeoutMs == other.actTimeoutMs && + browser == other.browser && + browserbaseSessionCreateParams == other.browserbaseSessionCreateParams && + browserbaseSessionId == other.browserbaseSessionId && + debugDom == other.debugDom && + domSettleTimeoutMs == other.domSettleTimeoutMs && + experimental == other.experimental && selfHeal == other.selfHeal && systemPrompt == other.systemPrompt && verbose == other.verbose && + waitForCaptchaSolves == other.waitForCaptchaSolves && additionalProperties == other.additionalProperties } private val hashCode: Int by lazy { Objects.hash( - browserbaseApiKey, - browserbaseProjectId, - domSettleTimeout, - model, + modelName, + actTimeoutMs, + browser, + browserbaseSessionCreateParams, + browserbaseSessionId, + debugDom, + domSettleTimeoutMs, + experimental, selfHeal, systemPrompt, verbose, + waitForCaptchaSolves, additionalProperties, ) } @@ -854,22 +1265,6149 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "Body{browserbaseApiKey=$browserbaseApiKey, browserbaseProjectId=$browserbaseProjectId, domSettleTimeout=$domSettleTimeout, model=$model, selfHeal=$selfHeal, systemPrompt=$systemPrompt, verbose=$verbose, additionalProperties=$additionalProperties}" + "Body{modelName=$modelName, actTimeoutMs=$actTimeoutMs, browser=$browser, browserbaseSessionCreateParams=$browserbaseSessionCreateParams, browserbaseSessionId=$browserbaseSessionId, debugDom=$debugDom, domSettleTimeoutMs=$domSettleTimeoutMs, experimental=$experimental, selfHeal=$selfHeal, systemPrompt=$systemPrompt, verbose=$verbose, waitForCaptchaSolves=$waitForCaptchaSolves, additionalProperties=$additionalProperties}" } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + class Browser + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val cdpUrl: JsonField, + private val launchOptions: JsonField, + private val type: JsonField, + private val additionalProperties: MutableMap, + ) { - return other is SessionStartParams && - body == other.body && - additionalHeaders == other.additionalHeaders && - additionalQueryParams == other.additionalQueryParams - } + @JsonCreator + private constructor( + @JsonProperty("cdpUrl") @ExcludeMissing cdpUrl: JsonField = JsonMissing.of(), + @JsonProperty("launchOptions") + @ExcludeMissing + launchOptions: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + ) : this(cdpUrl, launchOptions, type, mutableMapOf()) + + /** + * Chrome DevTools Protocol URL for connecting to existing browser + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun cdpUrl(): Optional = cdpUrl.getOptional("cdpUrl") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun launchOptions(): Optional = launchOptions.getOptional("launchOptions") + + /** + * Browser type to use + * + * @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") + + /** + * Returns the raw JSON value of [cdpUrl]. + * + * Unlike [cdpUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("cdpUrl") @ExcludeMissing fun _cdpUrl(): JsonField = cdpUrl + + /** + * Returns the raw JSON value of [launchOptions]. + * + * Unlike [launchOptions], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("launchOptions") + @ExcludeMissing + fun _launchOptions(): JsonField = launchOptions - override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Browser]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Browser]. */ + class Builder internal constructor() { + + private var cdpUrl: JsonField = JsonMissing.of() + private var launchOptions: JsonField = JsonMissing.of() + private var type: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(browser: Browser) = apply { + cdpUrl = browser.cdpUrl + launchOptions = browser.launchOptions + type = browser.type + additionalProperties = browser.additionalProperties.toMutableMap() + } + + /** Chrome DevTools Protocol URL for connecting to existing browser */ + fun cdpUrl(cdpUrl: String) = cdpUrl(JsonField.of(cdpUrl)) + + /** + * Sets [Builder.cdpUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.cdpUrl] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun cdpUrl(cdpUrl: JsonField) = apply { this.cdpUrl = cdpUrl } + + fun launchOptions(launchOptions: LaunchOptions) = + launchOptions(JsonField.of(launchOptions)) + + /** + * Sets [Builder.launchOptions] to an arbitrary JSON value. + * + * You should usually call [Builder.launchOptions] with a well-typed [LaunchOptions] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun launchOptions(launchOptions: JsonField) = apply { + this.launchOptions = launchOptions + } + + /** Browser type to use */ + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Browser]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Browser = + Browser(cdpUrl, launchOptions, type, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Browser = apply { + if (validated) { + return@apply + } + + cdpUrl() + launchOptions().ifPresent { it.validate() } + type().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 (cdpUrl.asKnown().isPresent) 1 else 0) + + (launchOptions.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + class LaunchOptions + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val acceptDownloads: JsonField, + private val args: JsonField>, + private val cdpUrl: JsonField, + private val chromiumSandbox: JsonField, + private val connectTimeoutMs: JsonField, + private val deviceScaleFactor: JsonField, + private val devtools: JsonField, + private val downloadsPath: JsonField, + private val executablePath: JsonField, + private val hasTouch: JsonField, + private val headless: JsonField, + private val ignoreDefaultArgs: JsonField, + private val ignoreHttpsErrors: JsonField, + private val locale: JsonField, + private val preserveUserDataDir: JsonField, + private val proxy: JsonField, + private val userDataDir: JsonField, + private val viewport: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("acceptDownloads") + @ExcludeMissing + acceptDownloads: JsonField = JsonMissing.of(), + @JsonProperty("args") + @ExcludeMissing + args: JsonField> = JsonMissing.of(), + @JsonProperty("cdpUrl") + @ExcludeMissing + cdpUrl: JsonField = JsonMissing.of(), + @JsonProperty("chromiumSandbox") + @ExcludeMissing + chromiumSandbox: JsonField = JsonMissing.of(), + @JsonProperty("connectTimeoutMs") + @ExcludeMissing + connectTimeoutMs: JsonField = JsonMissing.of(), + @JsonProperty("deviceScaleFactor") + @ExcludeMissing + deviceScaleFactor: JsonField = JsonMissing.of(), + @JsonProperty("devtools") + @ExcludeMissing + devtools: JsonField = JsonMissing.of(), + @JsonProperty("downloadsPath") + @ExcludeMissing + downloadsPath: JsonField = JsonMissing.of(), + @JsonProperty("executablePath") + @ExcludeMissing + executablePath: JsonField = JsonMissing.of(), + @JsonProperty("hasTouch") + @ExcludeMissing + hasTouch: JsonField = JsonMissing.of(), + @JsonProperty("headless") + @ExcludeMissing + headless: JsonField = JsonMissing.of(), + @JsonProperty("ignoreDefaultArgs") + @ExcludeMissing + ignoreDefaultArgs: JsonField = JsonMissing.of(), + @JsonProperty("ignoreHTTPSErrors") + @ExcludeMissing + ignoreHttpsErrors: JsonField = JsonMissing.of(), + @JsonProperty("locale") + @ExcludeMissing + locale: JsonField = JsonMissing.of(), + @JsonProperty("preserveUserDataDir") + @ExcludeMissing + preserveUserDataDir: JsonField = JsonMissing.of(), + @JsonProperty("proxy") @ExcludeMissing proxy: JsonField = JsonMissing.of(), + @JsonProperty("userDataDir") + @ExcludeMissing + userDataDir: JsonField = JsonMissing.of(), + @JsonProperty("viewport") + @ExcludeMissing + viewport: JsonField = JsonMissing.of(), + ) : this( + acceptDownloads, + args, + cdpUrl, + chromiumSandbox, + connectTimeoutMs, + deviceScaleFactor, + devtools, + downloadsPath, + executablePath, + hasTouch, + headless, + ignoreDefaultArgs, + ignoreHttpsErrors, + locale, + preserveUserDataDir, + proxy, + userDataDir, + viewport, + mutableMapOf(), + ) + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun acceptDownloads(): Optional = + acceptDownloads.getOptional("acceptDownloads") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun args(): Optional> = args.getOptional("args") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun cdpUrl(): Optional = cdpUrl.getOptional("cdpUrl") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun chromiumSandbox(): Optional = + chromiumSandbox.getOptional("chromiumSandbox") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun connectTimeoutMs(): Optional = + connectTimeoutMs.getOptional("connectTimeoutMs") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun deviceScaleFactor(): Optional = + deviceScaleFactor.getOptional("deviceScaleFactor") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun devtools(): Optional = devtools.getOptional("devtools") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun downloadsPath(): Optional = downloadsPath.getOptional("downloadsPath") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun executablePath(): Optional = executablePath.getOptional("executablePath") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun hasTouch(): Optional = hasTouch.getOptional("hasTouch") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun headless(): Optional = headless.getOptional("headless") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun ignoreDefaultArgs(): Optional = + ignoreDefaultArgs.getOptional("ignoreDefaultArgs") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun ignoreHttpsErrors(): Optional = + ignoreHttpsErrors.getOptional("ignoreHTTPSErrors") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun locale(): Optional = locale.getOptional("locale") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun preserveUserDataDir(): Optional = + preserveUserDataDir.getOptional("preserveUserDataDir") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun proxy(): Optional = proxy.getOptional("proxy") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun userDataDir(): Optional = userDataDir.getOptional("userDataDir") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun viewport(): Optional = viewport.getOptional("viewport") + + /** + * Returns the raw JSON value of [acceptDownloads]. + * + * Unlike [acceptDownloads], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("acceptDownloads") + @ExcludeMissing + fun _acceptDownloads(): JsonField = acceptDownloads + + /** + * Returns the raw JSON value of [args]. + * + * Unlike [args], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("args") @ExcludeMissing fun _args(): JsonField> = args + + /** + * Returns the raw JSON value of [cdpUrl]. + * + * Unlike [cdpUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("cdpUrl") @ExcludeMissing fun _cdpUrl(): JsonField = cdpUrl + + /** + * Returns the raw JSON value of [chromiumSandbox]. + * + * Unlike [chromiumSandbox], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("chromiumSandbox") + @ExcludeMissing + fun _chromiumSandbox(): JsonField = chromiumSandbox + + /** + * Returns the raw JSON value of [connectTimeoutMs]. + * + * Unlike [connectTimeoutMs], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("connectTimeoutMs") + @ExcludeMissing + fun _connectTimeoutMs(): JsonField = connectTimeoutMs + + /** + * Returns the raw JSON value of [deviceScaleFactor]. + * + * Unlike [deviceScaleFactor], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("deviceScaleFactor") + @ExcludeMissing + fun _deviceScaleFactor(): JsonField = deviceScaleFactor + + /** + * Returns the raw JSON value of [devtools]. + * + * Unlike [devtools], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("devtools") @ExcludeMissing fun _devtools(): JsonField = devtools + + /** + * Returns the raw JSON value of [downloadsPath]. + * + * Unlike [downloadsPath], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("downloadsPath") + @ExcludeMissing + fun _downloadsPath(): JsonField = downloadsPath + + /** + * Returns the raw JSON value of [executablePath]. + * + * Unlike [executablePath], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("executablePath") + @ExcludeMissing + fun _executablePath(): JsonField = executablePath + + /** + * Returns the raw JSON value of [hasTouch]. + * + * Unlike [hasTouch], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("hasTouch") @ExcludeMissing fun _hasTouch(): JsonField = hasTouch + + /** + * Returns the raw JSON value of [headless]. + * + * Unlike [headless], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("headless") @ExcludeMissing fun _headless(): JsonField = headless + + /** + * Returns the raw JSON value of [ignoreDefaultArgs]. + * + * Unlike [ignoreDefaultArgs], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("ignoreDefaultArgs") + @ExcludeMissing + fun _ignoreDefaultArgs(): JsonField = ignoreDefaultArgs + + /** + * Returns the raw JSON value of [ignoreHttpsErrors]. + * + * Unlike [ignoreHttpsErrors], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("ignoreHTTPSErrors") + @ExcludeMissing + fun _ignoreHttpsErrors(): JsonField = ignoreHttpsErrors + + /** + * Returns the raw JSON value of [locale]. + * + * Unlike [locale], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("locale") @ExcludeMissing fun _locale(): JsonField = locale + + /** + * Returns the raw JSON value of [preserveUserDataDir]. + * + * Unlike [preserveUserDataDir], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("preserveUserDataDir") + @ExcludeMissing + fun _preserveUserDataDir(): JsonField = preserveUserDataDir + + /** + * Returns the raw JSON value of [proxy]. + * + * Unlike [proxy], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("proxy") @ExcludeMissing fun _proxy(): JsonField = proxy + + /** + * Returns the raw JSON value of [userDataDir]. + * + * Unlike [userDataDir], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("userDataDir") + @ExcludeMissing + fun _userDataDir(): JsonField = userDataDir + + /** + * Returns the raw JSON value of [viewport]. + * + * Unlike [viewport], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("viewport") + @ExcludeMissing + fun _viewport(): JsonField = viewport + + @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 [LaunchOptions]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [LaunchOptions]. */ + class Builder internal constructor() { + + private var acceptDownloads: JsonField = JsonMissing.of() + private var args: JsonField>? = null + private var cdpUrl: JsonField = JsonMissing.of() + private var chromiumSandbox: JsonField = JsonMissing.of() + private var connectTimeoutMs: JsonField = JsonMissing.of() + private var deviceScaleFactor: JsonField = JsonMissing.of() + private var devtools: JsonField = JsonMissing.of() + private var downloadsPath: JsonField = JsonMissing.of() + private var executablePath: JsonField = JsonMissing.of() + private var hasTouch: JsonField = JsonMissing.of() + private var headless: JsonField = JsonMissing.of() + private var ignoreDefaultArgs: JsonField = JsonMissing.of() + private var ignoreHttpsErrors: JsonField = JsonMissing.of() + private var locale: JsonField = JsonMissing.of() + private var preserveUserDataDir: JsonField = JsonMissing.of() + private var proxy: JsonField = JsonMissing.of() + private var userDataDir: JsonField = JsonMissing.of() + private var viewport: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(launchOptions: LaunchOptions) = apply { + acceptDownloads = launchOptions.acceptDownloads + args = launchOptions.args.map { it.toMutableList() } + cdpUrl = launchOptions.cdpUrl + chromiumSandbox = launchOptions.chromiumSandbox + connectTimeoutMs = launchOptions.connectTimeoutMs + deviceScaleFactor = launchOptions.deviceScaleFactor + devtools = launchOptions.devtools + downloadsPath = launchOptions.downloadsPath + executablePath = launchOptions.executablePath + hasTouch = launchOptions.hasTouch + headless = launchOptions.headless + ignoreDefaultArgs = launchOptions.ignoreDefaultArgs + ignoreHttpsErrors = launchOptions.ignoreHttpsErrors + locale = launchOptions.locale + preserveUserDataDir = launchOptions.preserveUserDataDir + proxy = launchOptions.proxy + userDataDir = launchOptions.userDataDir + viewport = launchOptions.viewport + additionalProperties = launchOptions.additionalProperties.toMutableMap() + } + + fun acceptDownloads(acceptDownloads: Boolean) = + acceptDownloads(JsonField.of(acceptDownloads)) + + /** + * Sets [Builder.acceptDownloads] to an arbitrary JSON value. + * + * You should usually call [Builder.acceptDownloads] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun acceptDownloads(acceptDownloads: JsonField) = apply { + this.acceptDownloads = acceptDownloads + } + + fun args(args: List) = args(JsonField.of(args)) + + /** + * Sets [Builder.args] to an arbitrary JSON value. + * + * You should usually call [Builder.args] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun args(args: JsonField>) = apply { + this.args = args.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [args]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addArg(arg: String) = apply { + args = + (args ?: JsonField.of(mutableListOf())).also { + checkKnown("args", it).add(arg) + } + } + + fun cdpUrl(cdpUrl: String) = cdpUrl(JsonField.of(cdpUrl)) + + /** + * Sets [Builder.cdpUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.cdpUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun cdpUrl(cdpUrl: JsonField) = apply { this.cdpUrl = cdpUrl } + + fun chromiumSandbox(chromiumSandbox: Boolean) = + chromiumSandbox(JsonField.of(chromiumSandbox)) + + /** + * Sets [Builder.chromiumSandbox] to an arbitrary JSON value. + * + * You should usually call [Builder.chromiumSandbox] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun chromiumSandbox(chromiumSandbox: JsonField) = apply { + this.chromiumSandbox = chromiumSandbox + } + + fun connectTimeoutMs(connectTimeoutMs: Double) = + connectTimeoutMs(JsonField.of(connectTimeoutMs)) + + /** + * Sets [Builder.connectTimeoutMs] to an arbitrary JSON value. + * + * You should usually call [Builder.connectTimeoutMs] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun connectTimeoutMs(connectTimeoutMs: JsonField) = apply { + this.connectTimeoutMs = connectTimeoutMs + } + + fun deviceScaleFactor(deviceScaleFactor: Double) = + deviceScaleFactor(JsonField.of(deviceScaleFactor)) + + /** + * Sets [Builder.deviceScaleFactor] to an arbitrary JSON value. + * + * You should usually call [Builder.deviceScaleFactor] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun deviceScaleFactor(deviceScaleFactor: JsonField) = apply { + this.deviceScaleFactor = deviceScaleFactor + } + + fun devtools(devtools: Boolean) = devtools(JsonField.of(devtools)) + + /** + * Sets [Builder.devtools] to an arbitrary JSON value. + * + * You should usually call [Builder.devtools] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun devtools(devtools: JsonField) = apply { this.devtools = devtools } + + fun downloadsPath(downloadsPath: String) = + downloadsPath(JsonField.of(downloadsPath)) + + /** + * Sets [Builder.downloadsPath] to an arbitrary JSON value. + * + * You should usually call [Builder.downloadsPath] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun downloadsPath(downloadsPath: JsonField) = apply { + this.downloadsPath = downloadsPath + } + + fun executablePath(executablePath: String) = + executablePath(JsonField.of(executablePath)) + + /** + * Sets [Builder.executablePath] to an arbitrary JSON value. + * + * You should usually call [Builder.executablePath] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun executablePath(executablePath: JsonField) = apply { + this.executablePath = executablePath + } + + fun hasTouch(hasTouch: Boolean) = hasTouch(JsonField.of(hasTouch)) + + /** + * Sets [Builder.hasTouch] to an arbitrary JSON value. + * + * You should usually call [Builder.hasTouch] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun hasTouch(hasTouch: JsonField) = apply { this.hasTouch = hasTouch } + + fun headless(headless: Boolean) = headless(JsonField.of(headless)) + + /** + * Sets [Builder.headless] to an arbitrary JSON value. + * + * You should usually call [Builder.headless] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun headless(headless: JsonField) = apply { this.headless = headless } + + fun ignoreDefaultArgs(ignoreDefaultArgs: IgnoreDefaultArgs) = + ignoreDefaultArgs(JsonField.of(ignoreDefaultArgs)) + + /** + * Sets [Builder.ignoreDefaultArgs] to an arbitrary JSON value. + * + * You should usually call [Builder.ignoreDefaultArgs] with a well-typed + * [IgnoreDefaultArgs] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun ignoreDefaultArgs(ignoreDefaultArgs: JsonField) = apply { + this.ignoreDefaultArgs = ignoreDefaultArgs + } + + /** Alias for calling [ignoreDefaultArgs] with `IgnoreDefaultArgs.ofBool(bool)`. */ + fun ignoreDefaultArgs(bool: Boolean) = + ignoreDefaultArgs(IgnoreDefaultArgs.ofBool(bool)) + + /** + * Alias for calling [ignoreDefaultArgs] with + * `IgnoreDefaultArgs.ofStrings(strings)`. + */ + fun ignoreDefaultArgsOfStrings(strings: List) = + ignoreDefaultArgs(IgnoreDefaultArgs.ofStrings(strings)) + + fun ignoreHttpsErrors(ignoreHttpsErrors: Boolean) = + ignoreHttpsErrors(JsonField.of(ignoreHttpsErrors)) + + /** + * Sets [Builder.ignoreHttpsErrors] to an arbitrary JSON value. + * + * You should usually call [Builder.ignoreHttpsErrors] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun ignoreHttpsErrors(ignoreHttpsErrors: JsonField) = apply { + this.ignoreHttpsErrors = ignoreHttpsErrors + } + + fun locale(locale: String) = locale(JsonField.of(locale)) + + /** + * Sets [Builder.locale] to an arbitrary JSON value. + * + * You should usually call [Builder.locale] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun locale(locale: JsonField) = apply { this.locale = locale } + + fun preserveUserDataDir(preserveUserDataDir: Boolean) = + preserveUserDataDir(JsonField.of(preserveUserDataDir)) + + /** + * Sets [Builder.preserveUserDataDir] to an arbitrary JSON value. + * + * You should usually call [Builder.preserveUserDataDir] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun preserveUserDataDir(preserveUserDataDir: JsonField) = apply { + this.preserveUserDataDir = preserveUserDataDir + } + + fun proxy(proxy: Proxy) = proxy(JsonField.of(proxy)) + + /** + * Sets [Builder.proxy] to an arbitrary JSON value. + * + * You should usually call [Builder.proxy] with a well-typed [Proxy] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun proxy(proxy: JsonField) = apply { this.proxy = proxy } + + fun userDataDir(userDataDir: String) = userDataDir(JsonField.of(userDataDir)) + + /** + * Sets [Builder.userDataDir] to an arbitrary JSON value. + * + * You should usually call [Builder.userDataDir] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun userDataDir(userDataDir: JsonField) = apply { + this.userDataDir = userDataDir + } + + fun viewport(viewport: Viewport) = viewport(JsonField.of(viewport)) + + /** + * Sets [Builder.viewport] to an arbitrary JSON value. + * + * You should usually call [Builder.viewport] with a well-typed [Viewport] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun viewport(viewport: JsonField) = apply { this.viewport = viewport } + + 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 [LaunchOptions]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): LaunchOptions = + LaunchOptions( + acceptDownloads, + (args ?: JsonMissing.of()).map { it.toImmutable() }, + cdpUrl, + chromiumSandbox, + connectTimeoutMs, + deviceScaleFactor, + devtools, + downloadsPath, + executablePath, + hasTouch, + headless, + ignoreDefaultArgs, + ignoreHttpsErrors, + locale, + preserveUserDataDir, + proxy, + userDataDir, + viewport, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): LaunchOptions = apply { + if (validated) { + return@apply + } + + acceptDownloads() + args() + cdpUrl() + chromiumSandbox() + connectTimeoutMs() + deviceScaleFactor() + devtools() + downloadsPath() + executablePath() + hasTouch() + headless() + ignoreDefaultArgs().ifPresent { it.validate() } + ignoreHttpsErrors() + locale() + preserveUserDataDir() + proxy().ifPresent { it.validate() } + userDataDir() + viewport().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 (acceptDownloads.asKnown().isPresent) 1 else 0) + + (args.asKnown().getOrNull()?.size ?: 0) + + (if (cdpUrl.asKnown().isPresent) 1 else 0) + + (if (chromiumSandbox.asKnown().isPresent) 1 else 0) + + (if (connectTimeoutMs.asKnown().isPresent) 1 else 0) + + (if (deviceScaleFactor.asKnown().isPresent) 1 else 0) + + (if (devtools.asKnown().isPresent) 1 else 0) + + (if (downloadsPath.asKnown().isPresent) 1 else 0) + + (if (executablePath.asKnown().isPresent) 1 else 0) + + (if (hasTouch.asKnown().isPresent) 1 else 0) + + (if (headless.asKnown().isPresent) 1 else 0) + + (ignoreDefaultArgs.asKnown().getOrNull()?.validity() ?: 0) + + (if (ignoreHttpsErrors.asKnown().isPresent) 1 else 0) + + (if (locale.asKnown().isPresent) 1 else 0) + + (if (preserveUserDataDir.asKnown().isPresent) 1 else 0) + + (proxy.asKnown().getOrNull()?.validity() ?: 0) + + (if (userDataDir.asKnown().isPresent) 1 else 0) + + (viewport.asKnown().getOrNull()?.validity() ?: 0) + + @JsonDeserialize(using = IgnoreDefaultArgs.Deserializer::class) + @JsonSerialize(using = IgnoreDefaultArgs.Serializer::class) + class IgnoreDefaultArgs + private constructor( + private val bool: Boolean? = null, + private val strings: List? = null, + private val _json: JsonValue? = null, + ) { + + fun bool(): Optional = Optional.ofNullable(bool) + + fun strings(): Optional> = Optional.ofNullable(strings) + + fun isBool(): Boolean = bool != null + + fun isStrings(): Boolean = strings != null + + fun asBool(): Boolean = bool.getOrThrow("bool") + + fun asStrings(): List = strings.getOrThrow("strings") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + bool != null -> visitor.visitBool(bool) + strings != null -> visitor.visitStrings(strings) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): IgnoreDefaultArgs = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitBool(bool: Boolean) {} + + 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 visitBool(bool: Boolean) = 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 IgnoreDefaultArgs && + bool == other.bool && + strings == other.strings + } + + override fun hashCode(): Int = Objects.hash(bool, strings) + + override fun toString(): String = + when { + bool != null -> "IgnoreDefaultArgs{bool=$bool}" + strings != null -> "IgnoreDefaultArgs{strings=$strings}" + _json != null -> "IgnoreDefaultArgs{_unknown=$_json}" + else -> throw IllegalStateException("Invalid IgnoreDefaultArgs") + } + + companion object { + + @JvmStatic fun ofBool(bool: Boolean) = IgnoreDefaultArgs(bool = bool) + + @JvmStatic + fun ofStrings(strings: List) = + IgnoreDefaultArgs(strings = strings.toImmutable()) + } + + /** + * An interface that defines how to map each variant of [IgnoreDefaultArgs] to a + * value of type [T]. + */ + interface Visitor { + + fun visitBool(bool: Boolean): T + + fun visitStrings(strings: List): T + + /** + * Maps an unknown variant of [IgnoreDefaultArgs] to a value of type [T]. + * + * An instance of [IgnoreDefaultArgs] 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 IgnoreDefaultArgs: $json") + } + } + + internal class Deserializer : + BaseDeserializer(IgnoreDefaultArgs::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): IgnoreDefaultArgs { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + IgnoreDefaultArgs(bool = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>())?.let { + IgnoreDefaultArgs(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 + // string). + 0 -> IgnoreDefaultArgs(_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(IgnoreDefaultArgs::class) { + + override fun serialize( + value: IgnoreDefaultArgs, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.bool != null -> generator.writeObject(value.bool) + value.strings != null -> generator.writeObject(value.strings) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid IgnoreDefaultArgs") + } + } + } + } + + class Proxy + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val server: JsonField, + private val bypass: JsonField, + private val password: JsonField, + private val username: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("server") + @ExcludeMissing + server: JsonField = JsonMissing.of(), + @JsonProperty("bypass") + @ExcludeMissing + bypass: JsonField = JsonMissing.of(), + @JsonProperty("password") + @ExcludeMissing + password: JsonField = JsonMissing.of(), + @JsonProperty("username") + @ExcludeMissing + username: JsonField = JsonMissing.of(), + ) : this(server, bypass, password, username, 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 server(): String = server.getRequired("server") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun bypass(): Optional = bypass.getOptional("bypass") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun password(): Optional = password.getOptional("password") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun username(): Optional = username.getOptional("username") + + /** + * Returns the raw JSON value of [server]. + * + * Unlike [server], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("server") @ExcludeMissing fun _server(): JsonField = server + + /** + * Returns the raw JSON value of [bypass]. + * + * Unlike [bypass], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("bypass") @ExcludeMissing fun _bypass(): JsonField = bypass + + /** + * Returns the raw JSON value of [password]. + * + * Unlike [password], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("password") + @ExcludeMissing + fun _password(): JsonField = password + + /** + * Returns the raw JSON value of [username]. + * + * Unlike [username], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("username") + @ExcludeMissing + fun _username(): JsonField = username + + @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 [Proxy]. + * + * The following fields are required: + * ```java + * .server() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Proxy]. */ + class Builder internal constructor() { + + private var server: JsonField? = null + private var bypass: JsonField = JsonMissing.of() + private var password: JsonField = JsonMissing.of() + private var username: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(proxy: Proxy) = apply { + server = proxy.server + bypass = proxy.bypass + password = proxy.password + username = proxy.username + additionalProperties = proxy.additionalProperties.toMutableMap() + } + + fun server(server: String) = server(JsonField.of(server)) + + /** + * Sets [Builder.server] to an arbitrary JSON value. + * + * You should usually call [Builder.server] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun server(server: JsonField) = apply { this.server = server } + + fun bypass(bypass: String) = bypass(JsonField.of(bypass)) + + /** + * Sets [Builder.bypass] to an arbitrary JSON value. + * + * You should usually call [Builder.bypass] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun bypass(bypass: JsonField) = apply { this.bypass = bypass } + + fun password(password: String) = password(JsonField.of(password)) + + /** + * Sets [Builder.password] to an arbitrary JSON value. + * + * You should usually call [Builder.password] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun password(password: JsonField) = apply { this.password = password } + + fun username(username: String) = username(JsonField.of(username)) + + /** + * Sets [Builder.username] to an arbitrary JSON value. + * + * You should usually call [Builder.username] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun username(username: JsonField) = apply { this.username = username } + + 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 [Proxy]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .server() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Proxy = + Proxy( + checkRequired("server", server), + bypass, + password, + username, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Proxy = apply { + if (validated) { + return@apply + } + + server() + bypass() + password() + username() + 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 (server.asKnown().isPresent) 1 else 0) + + (if (bypass.asKnown().isPresent) 1 else 0) + + (if (password.asKnown().isPresent) 1 else 0) + + (if (username.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Proxy && + server == other.server && + bypass == other.bypass && + password == other.password && + username == other.username && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(server, bypass, password, username, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Proxy{server=$server, bypass=$bypass, password=$password, username=$username, additionalProperties=$additionalProperties}" + } + + class Viewport + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val height: JsonField, + private val width: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("height") + @ExcludeMissing + height: JsonField = JsonMissing.of(), + @JsonProperty("width") + @ExcludeMissing + width: JsonField = JsonMissing.of(), + ) : this(height, width, 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 height(): Double = height.getRequired("height") + + /** + * @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 width(): Double = width.getRequired("width") + + /** + * Returns the raw JSON value of [height]. + * + * Unlike [height], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("height") @ExcludeMissing fun _height(): JsonField = height + + /** + * Returns the raw JSON value of [width]. + * + * Unlike [width], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("width") @ExcludeMissing fun _width(): JsonField = width + + @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 [Viewport]. + * + * The following fields are required: + * ```java + * .height() + * .width() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Viewport]. */ + class Builder internal constructor() { + + private var height: JsonField? = null + private var width: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(viewport: Viewport) = apply { + height = viewport.height + width = viewport.width + additionalProperties = viewport.additionalProperties.toMutableMap() + } + + fun height(height: Double) = height(JsonField.of(height)) + + /** + * Sets [Builder.height] to an arbitrary JSON value. + * + * You should usually call [Builder.height] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun height(height: JsonField) = apply { this.height = height } + + fun width(width: Double) = width(JsonField.of(width)) + + /** + * Sets [Builder.width] to an arbitrary JSON value. + * + * You should usually call [Builder.width] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun width(width: JsonField) = apply { this.width = width } + + 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 [Viewport]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .height() + * .width() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Viewport = + Viewport( + checkRequired("height", height), + checkRequired("width", width), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Viewport = apply { + if (validated) { + return@apply + } + + height() + width() + 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 (height.asKnown().isPresent) 1 else 0) + + (if (width.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Viewport && + height == other.height && + width == other.width && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(height, width, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Viewport{height=$height, width=$width, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is LaunchOptions && + acceptDownloads == other.acceptDownloads && + args == other.args && + cdpUrl == other.cdpUrl && + chromiumSandbox == other.chromiumSandbox && + connectTimeoutMs == other.connectTimeoutMs && + deviceScaleFactor == other.deviceScaleFactor && + devtools == other.devtools && + downloadsPath == other.downloadsPath && + executablePath == other.executablePath && + hasTouch == other.hasTouch && + headless == other.headless && + ignoreDefaultArgs == other.ignoreDefaultArgs && + ignoreHttpsErrors == other.ignoreHttpsErrors && + locale == other.locale && + preserveUserDataDir == other.preserveUserDataDir && + proxy == other.proxy && + userDataDir == other.userDataDir && + viewport == other.viewport && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + acceptDownloads, + args, + cdpUrl, + chromiumSandbox, + connectTimeoutMs, + deviceScaleFactor, + devtools, + downloadsPath, + executablePath, + hasTouch, + headless, + ignoreDefaultArgs, + ignoreHttpsErrors, + locale, + preserveUserDataDir, + proxy, + userDataDir, + viewport, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "LaunchOptions{acceptDownloads=$acceptDownloads, args=$args, cdpUrl=$cdpUrl, chromiumSandbox=$chromiumSandbox, connectTimeoutMs=$connectTimeoutMs, deviceScaleFactor=$deviceScaleFactor, devtools=$devtools, downloadsPath=$downloadsPath, executablePath=$executablePath, hasTouch=$hasTouch, headless=$headless, ignoreDefaultArgs=$ignoreDefaultArgs, ignoreHttpsErrors=$ignoreHttpsErrors, locale=$locale, preserveUserDataDir=$preserveUserDataDir, proxy=$proxy, userDataDir=$userDataDir, viewport=$viewport, additionalProperties=$additionalProperties}" + } + + /** Browser type to use */ + 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 LOCAL = of("local") + + @JvmField val BROWSERBASE = of("browserbase") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + LOCAL, + BROWSERBASE, + } + + /** + * 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 { + LOCAL, + BROWSERBASE, + /** 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) { + LOCAL -> Value.LOCAL + BROWSERBASE -> Value.BROWSERBASE + 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) { + LOCAL -> Known.LOCAL + BROWSERBASE -> Known.BROWSERBASE + 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 + + 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 Browser && + cdpUrl == other.cdpUrl && + launchOptions == other.launchOptions && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(cdpUrl, launchOptions, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Browser{cdpUrl=$cdpUrl, launchOptions=$launchOptions, type=$type, additionalProperties=$additionalProperties}" + } + + class BrowserbaseSessionCreateParams + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val browserSettings: JsonField, + private val extensionId: JsonField, + private val keepAlive: JsonField, + private val projectId: JsonField, + private val proxies: JsonField, + private val region: JsonField, + private val timeout: JsonField, + private val userMetadata: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("browserSettings") + @ExcludeMissing + browserSettings: JsonField = JsonMissing.of(), + @JsonProperty("extensionId") + @ExcludeMissing + extensionId: JsonField = JsonMissing.of(), + @JsonProperty("keepAlive") + @ExcludeMissing + keepAlive: JsonField = JsonMissing.of(), + @JsonProperty("projectId") + @ExcludeMissing + projectId: JsonField = JsonMissing.of(), + @JsonProperty("proxies") @ExcludeMissing proxies: JsonField = JsonMissing.of(), + @JsonProperty("region") @ExcludeMissing region: JsonField = JsonMissing.of(), + @JsonProperty("timeout") @ExcludeMissing timeout: JsonField = JsonMissing.of(), + @JsonProperty("userMetadata") + @ExcludeMissing + userMetadata: JsonField = JsonMissing.of(), + ) : this( + browserSettings, + extensionId, + keepAlive, + projectId, + proxies, + region, + timeout, + userMetadata, + mutableMapOf(), + ) + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun browserSettings(): Optional = + browserSettings.getOptional("browserSettings") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun extensionId(): Optional = extensionId.getOptional("extensionId") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun keepAlive(): Optional = keepAlive.getOptional("keepAlive") + + /** + * @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") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun proxies(): Optional = proxies.getOptional("proxies") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun region(): Optional = region.getOptional("region") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun timeout(): Optional = timeout.getOptional("timeout") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun userMetadata(): Optional = userMetadata.getOptional("userMetadata") + + /** + * Returns the raw JSON value of [browserSettings]. + * + * Unlike [browserSettings], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("browserSettings") + @ExcludeMissing + fun _browserSettings(): JsonField = browserSettings + + /** + * Returns the raw JSON value of [extensionId]. + * + * Unlike [extensionId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("extensionId") + @ExcludeMissing + fun _extensionId(): JsonField = extensionId + + /** + * Returns the raw JSON value of [keepAlive]. + * + * Unlike [keepAlive], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("keepAlive") @ExcludeMissing fun _keepAlive(): JsonField = keepAlive + + /** + * 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 [proxies]. + * + * Unlike [proxies], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("proxies") @ExcludeMissing fun _proxies(): JsonField = proxies + + /** + * Returns the raw JSON value of [region]. + * + * Unlike [region], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("region") @ExcludeMissing fun _region(): JsonField = region + + /** + * Returns the raw JSON value of [timeout]. + * + * Unlike [timeout], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("timeout") @ExcludeMissing fun _timeout(): JsonField = timeout + + /** + * Returns the raw JSON value of [userMetadata]. + * + * Unlike [userMetadata], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("userMetadata") + @ExcludeMissing + fun _userMetadata(): JsonField = userMetadata + + @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 + * [BrowserbaseSessionCreateParams]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [BrowserbaseSessionCreateParams]. */ + class Builder internal constructor() { + + private var browserSettings: JsonField = JsonMissing.of() + private var extensionId: JsonField = JsonMissing.of() + private var keepAlive: JsonField = JsonMissing.of() + private var projectId: JsonField = JsonMissing.of() + private var proxies: JsonField = JsonMissing.of() + private var region: JsonField = JsonMissing.of() + private var timeout: JsonField = JsonMissing.of() + private var userMetadata: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(browserbaseSessionCreateParams: BrowserbaseSessionCreateParams) = + apply { + browserSettings = browserbaseSessionCreateParams.browserSettings + extensionId = browserbaseSessionCreateParams.extensionId + keepAlive = browserbaseSessionCreateParams.keepAlive + projectId = browserbaseSessionCreateParams.projectId + proxies = browserbaseSessionCreateParams.proxies + region = browserbaseSessionCreateParams.region + timeout = browserbaseSessionCreateParams.timeout + userMetadata = browserbaseSessionCreateParams.userMetadata + additionalProperties = + browserbaseSessionCreateParams.additionalProperties.toMutableMap() + } + + fun browserSettings(browserSettings: BrowserSettings) = + browserSettings(JsonField.of(browserSettings)) + + /** + * Sets [Builder.browserSettings] to an arbitrary JSON value. + * + * You should usually call [Builder.browserSettings] with a well-typed [BrowserSettings] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun browserSettings(browserSettings: JsonField) = apply { + this.browserSettings = browserSettings + } + + fun extensionId(extensionId: String) = extensionId(JsonField.of(extensionId)) + + /** + * Sets [Builder.extensionId] to an arbitrary JSON value. + * + * You should usually call [Builder.extensionId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun extensionId(extensionId: JsonField) = apply { + this.extensionId = extensionId + } + + fun keepAlive(keepAlive: Boolean) = keepAlive(JsonField.of(keepAlive)) + + /** + * Sets [Builder.keepAlive] to an arbitrary JSON value. + * + * You should usually call [Builder.keepAlive] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun keepAlive(keepAlive: JsonField) = apply { this.keepAlive = keepAlive } + + 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 proxies(proxies: Proxies) = proxies(JsonField.of(proxies)) + + /** + * Sets [Builder.proxies] to an arbitrary JSON value. + * + * You should usually call [Builder.proxies] with a well-typed [Proxies] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun proxies(proxies: JsonField) = apply { this.proxies = proxies } + + /** Alias for calling [proxies] with `Proxies.ofBool(bool)`. */ + fun proxies(bool: Boolean) = proxies(Proxies.ofBool(bool)) + + /** Alias for calling [proxies] with `Proxies.ofProxyConfigList(proxyConfigList)`. */ + fun proxiesOfProxyConfigList(proxyConfigList: List) = + proxies(Proxies.ofProxyConfigList(proxyConfigList)) + + fun region(region: Region) = region(JsonField.of(region)) + + /** + * Sets [Builder.region] to an arbitrary JSON value. + * + * You should usually call [Builder.region] with a well-typed [Region] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun region(region: JsonField) = apply { this.region = region } + + fun timeout(timeout: Double) = timeout(JsonField.of(timeout)) + + /** + * Sets [Builder.timeout] to an arbitrary JSON value. + * + * You should usually call [Builder.timeout] with a well-typed [Double] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun timeout(timeout: JsonField) = apply { this.timeout = timeout } + + fun userMetadata(userMetadata: UserMetadata) = userMetadata(JsonField.of(userMetadata)) + + /** + * Sets [Builder.userMetadata] to an arbitrary JSON value. + * + * You should usually call [Builder.userMetadata] with a well-typed [UserMetadata] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun userMetadata(userMetadata: JsonField) = apply { + this.userMetadata = userMetadata + } + + 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 [BrowserbaseSessionCreateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): BrowserbaseSessionCreateParams = + BrowserbaseSessionCreateParams( + browserSettings, + extensionId, + keepAlive, + projectId, + proxies, + region, + timeout, + userMetadata, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BrowserbaseSessionCreateParams = apply { + if (validated) { + return@apply + } + + browserSettings().ifPresent { it.validate() } + extensionId() + keepAlive() + projectId() + proxies().ifPresent { it.validate() } + region().ifPresent { it.validate() } + timeout() + userMetadata().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 = + (browserSettings.asKnown().getOrNull()?.validity() ?: 0) + + (if (extensionId.asKnown().isPresent) 1 else 0) + + (if (keepAlive.asKnown().isPresent) 1 else 0) + + (if (projectId.asKnown().isPresent) 1 else 0) + + (proxies.asKnown().getOrNull()?.validity() ?: 0) + + (region.asKnown().getOrNull()?.validity() ?: 0) + + (if (timeout.asKnown().isPresent) 1 else 0) + + (userMetadata.asKnown().getOrNull()?.validity() ?: 0) + + class BrowserSettings + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val advancedStealth: JsonField, + private val blockAds: JsonField, + private val context: JsonField, + private val extensionId: JsonField, + private val fingerprint: JsonField, + private val logSession: JsonField, + private val recordSession: JsonField, + private val solveCaptchas: JsonField, + private val viewport: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("advancedStealth") + @ExcludeMissing + advancedStealth: JsonField = JsonMissing.of(), + @JsonProperty("blockAds") + @ExcludeMissing + blockAds: JsonField = JsonMissing.of(), + @JsonProperty("context") + @ExcludeMissing + context: JsonField = JsonMissing.of(), + @JsonProperty("extensionId") + @ExcludeMissing + extensionId: JsonField = JsonMissing.of(), + @JsonProperty("fingerprint") + @ExcludeMissing + fingerprint: JsonField = JsonMissing.of(), + @JsonProperty("logSession") + @ExcludeMissing + logSession: JsonField = JsonMissing.of(), + @JsonProperty("recordSession") + @ExcludeMissing + recordSession: JsonField = JsonMissing.of(), + @JsonProperty("solveCaptchas") + @ExcludeMissing + solveCaptchas: JsonField = JsonMissing.of(), + @JsonProperty("viewport") + @ExcludeMissing + viewport: JsonField = JsonMissing.of(), + ) : this( + advancedStealth, + blockAds, + context, + extensionId, + fingerprint, + logSession, + recordSession, + solveCaptchas, + viewport, + mutableMapOf(), + ) + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun advancedStealth(): Optional = + advancedStealth.getOptional("advancedStealth") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun blockAds(): Optional = blockAds.getOptional("blockAds") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun context(): Optional = context.getOptional("context") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun extensionId(): Optional = extensionId.getOptional("extensionId") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun fingerprint(): Optional = fingerprint.getOptional("fingerprint") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun logSession(): Optional = logSession.getOptional("logSession") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun recordSession(): Optional = recordSession.getOptional("recordSession") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun solveCaptchas(): Optional = solveCaptchas.getOptional("solveCaptchas") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun viewport(): Optional = viewport.getOptional("viewport") + + /** + * Returns the raw JSON value of [advancedStealth]. + * + * Unlike [advancedStealth], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("advancedStealth") + @ExcludeMissing + fun _advancedStealth(): JsonField = advancedStealth + + /** + * Returns the raw JSON value of [blockAds]. + * + * Unlike [blockAds], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("blockAds") @ExcludeMissing fun _blockAds(): JsonField = blockAds + + /** + * Returns the raw JSON value of [context]. + * + * Unlike [context], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("context") @ExcludeMissing fun _context(): JsonField = context + + /** + * Returns the raw JSON value of [extensionId]. + * + * Unlike [extensionId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("extensionId") + @ExcludeMissing + fun _extensionId(): JsonField = extensionId + + /** + * Returns the raw JSON value of [fingerprint]. + * + * Unlike [fingerprint], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("fingerprint") + @ExcludeMissing + fun _fingerprint(): JsonField = fingerprint + + /** + * Returns the raw JSON value of [logSession]. + * + * Unlike [logSession], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("logSession") + @ExcludeMissing + fun _logSession(): JsonField = logSession + + /** + * Returns the raw JSON value of [recordSession]. + * + * Unlike [recordSession], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("recordSession") + @ExcludeMissing + fun _recordSession(): JsonField = recordSession + + /** + * Returns the raw JSON value of [solveCaptchas]. + * + * Unlike [solveCaptchas], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("solveCaptchas") + @ExcludeMissing + fun _solveCaptchas(): JsonField = solveCaptchas + + /** + * Returns the raw JSON value of [viewport]. + * + * Unlike [viewport], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("viewport") + @ExcludeMissing + fun _viewport(): JsonField = viewport + + @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 [BrowserSettings]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [BrowserSettings]. */ + class Builder internal constructor() { + + private var advancedStealth: JsonField = JsonMissing.of() + private var blockAds: JsonField = JsonMissing.of() + private var context: JsonField = JsonMissing.of() + private var extensionId: JsonField = JsonMissing.of() + private var fingerprint: JsonField = JsonMissing.of() + private var logSession: JsonField = JsonMissing.of() + private var recordSession: JsonField = JsonMissing.of() + private var solveCaptchas: JsonField = JsonMissing.of() + private var viewport: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(browserSettings: BrowserSettings) = apply { + advancedStealth = browserSettings.advancedStealth + blockAds = browserSettings.blockAds + context = browserSettings.context + extensionId = browserSettings.extensionId + fingerprint = browserSettings.fingerprint + logSession = browserSettings.logSession + recordSession = browserSettings.recordSession + solveCaptchas = browserSettings.solveCaptchas + viewport = browserSettings.viewport + additionalProperties = browserSettings.additionalProperties.toMutableMap() + } + + fun advancedStealth(advancedStealth: Boolean) = + advancedStealth(JsonField.of(advancedStealth)) + + /** + * Sets [Builder.advancedStealth] to an arbitrary JSON value. + * + * You should usually call [Builder.advancedStealth] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun advancedStealth(advancedStealth: JsonField) = apply { + this.advancedStealth = advancedStealth + } + + fun blockAds(blockAds: Boolean) = blockAds(JsonField.of(blockAds)) + + /** + * Sets [Builder.blockAds] to an arbitrary JSON value. + * + * You should usually call [Builder.blockAds] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun blockAds(blockAds: JsonField) = apply { this.blockAds = blockAds } + + fun context(context: Context) = context(JsonField.of(context)) + + /** + * Sets [Builder.context] to an arbitrary JSON value. + * + * You should usually call [Builder.context] with a well-typed [Context] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun context(context: JsonField) = apply { this.context = context } + + fun extensionId(extensionId: String) = extensionId(JsonField.of(extensionId)) + + /** + * Sets [Builder.extensionId] to an arbitrary JSON value. + * + * You should usually call [Builder.extensionId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun extensionId(extensionId: JsonField) = apply { + this.extensionId = extensionId + } + + fun fingerprint(fingerprint: Fingerprint) = fingerprint(JsonField.of(fingerprint)) + + /** + * Sets [Builder.fingerprint] to an arbitrary JSON value. + * + * You should usually call [Builder.fingerprint] with a well-typed [Fingerprint] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun fingerprint(fingerprint: JsonField) = apply { + this.fingerprint = fingerprint + } + + fun logSession(logSession: Boolean) = logSession(JsonField.of(logSession)) + + /** + * Sets [Builder.logSession] to an arbitrary JSON value. + * + * You should usually call [Builder.logSession] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun logSession(logSession: JsonField) = apply { + this.logSession = logSession + } + + fun recordSession(recordSession: Boolean) = + recordSession(JsonField.of(recordSession)) + + /** + * Sets [Builder.recordSession] to an arbitrary JSON value. + * + * You should usually call [Builder.recordSession] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun recordSession(recordSession: JsonField) = apply { + this.recordSession = recordSession + } + + fun solveCaptchas(solveCaptchas: Boolean) = + solveCaptchas(JsonField.of(solveCaptchas)) + + /** + * Sets [Builder.solveCaptchas] to an arbitrary JSON value. + * + * You should usually call [Builder.solveCaptchas] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun solveCaptchas(solveCaptchas: JsonField) = apply { + this.solveCaptchas = solveCaptchas + } + + fun viewport(viewport: Viewport) = viewport(JsonField.of(viewport)) + + /** + * Sets [Builder.viewport] to an arbitrary JSON value. + * + * You should usually call [Builder.viewport] with a well-typed [Viewport] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun viewport(viewport: JsonField) = apply { this.viewport = viewport } + + 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 [BrowserSettings]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): BrowserSettings = + BrowserSettings( + advancedStealth, + blockAds, + context, + extensionId, + fingerprint, + logSession, + recordSession, + solveCaptchas, + viewport, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BrowserSettings = apply { + if (validated) { + return@apply + } + + advancedStealth() + blockAds() + context().ifPresent { it.validate() } + extensionId() + fingerprint().ifPresent { it.validate() } + logSession() + recordSession() + solveCaptchas() + viewport().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 (advancedStealth.asKnown().isPresent) 1 else 0) + + (if (blockAds.asKnown().isPresent) 1 else 0) + + (context.asKnown().getOrNull()?.validity() ?: 0) + + (if (extensionId.asKnown().isPresent) 1 else 0) + + (fingerprint.asKnown().getOrNull()?.validity() ?: 0) + + (if (logSession.asKnown().isPresent) 1 else 0) + + (if (recordSession.asKnown().isPresent) 1 else 0) + + (if (solveCaptchas.asKnown().isPresent) 1 else 0) + + (viewport.asKnown().getOrNull()?.validity() ?: 0) + + class Context + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val id: JsonField, + private val persist: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("persist") + @ExcludeMissing + persist: JsonField = JsonMissing.of(), + ) : this(id, persist, 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 id(): String = id.getRequired("id") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun persist(): Optional = persist.getOptional("persist") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [persist]. + * + * Unlike [persist], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("persist") + @ExcludeMissing + fun _persist(): JsonField = persist + + @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 [Context]. + * + * The following fields are required: + * ```java + * .id() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Context]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var persist: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(context: Context) = apply { + id = context.id + persist = context.persist + additionalProperties = context.additionalProperties.toMutableMap() + } + + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + fun persist(persist: Boolean) = persist(JsonField.of(persist)) + + /** + * Sets [Builder.persist] to an arbitrary JSON value. + * + * You should usually call [Builder.persist] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun persist(persist: JsonField) = apply { this.persist = persist } + + 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 [Context]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .id() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Context = + Context( + checkRequired("id", id), + persist, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Context = apply { + if (validated) { + return@apply + } + + id() + persist() + 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 (id.asKnown().isPresent) 1 else 0) + + (if (persist.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Context && + id == other.id && + persist == other.persist && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(id, persist, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Context{id=$id, persist=$persist, additionalProperties=$additionalProperties}" + } + + class Fingerprint + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val browsers: JsonField>, + private val devices: JsonField>, + private val httpVersion: JsonField, + private val locales: JsonField>, + private val operatingSystems: JsonField>, + private val screen: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("browsers") + @ExcludeMissing + browsers: JsonField> = JsonMissing.of(), + @JsonProperty("devices") + @ExcludeMissing + devices: JsonField> = JsonMissing.of(), + @JsonProperty("httpVersion") + @ExcludeMissing + httpVersion: JsonField = JsonMissing.of(), + @JsonProperty("locales") + @ExcludeMissing + locales: JsonField> = JsonMissing.of(), + @JsonProperty("operatingSystems") + @ExcludeMissing + operatingSystems: JsonField> = JsonMissing.of(), + @JsonProperty("screen") + @ExcludeMissing + screen: JsonField = JsonMissing.of(), + ) : this( + browsers, + devices, + httpVersion, + locales, + operatingSystems, + screen, + mutableMapOf(), + ) + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun browsers(): Optional> = browsers.getOptional("browsers") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun devices(): Optional> = devices.getOptional("devices") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun httpVersion(): Optional = httpVersion.getOptional("httpVersion") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun locales(): Optional> = locales.getOptional("locales") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun operatingSystems(): Optional> = + operatingSystems.getOptional("operatingSystems") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun screen(): Optional = screen.getOptional("screen") + + /** + * Returns the raw JSON value of [browsers]. + * + * Unlike [browsers], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("browsers") + @ExcludeMissing + fun _browsers(): JsonField> = browsers + + /** + * Returns the raw JSON value of [devices]. + * + * Unlike [devices], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("devices") + @ExcludeMissing + fun _devices(): JsonField> = devices + + /** + * Returns the raw JSON value of [httpVersion]. + * + * Unlike [httpVersion], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("httpVersion") + @ExcludeMissing + fun _httpVersion(): JsonField = httpVersion + + /** + * Returns the raw JSON value of [locales]. + * + * Unlike [locales], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("locales") + @ExcludeMissing + fun _locales(): JsonField> = locales + + /** + * Returns the raw JSON value of [operatingSystems]. + * + * Unlike [operatingSystems], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("operatingSystems") + @ExcludeMissing + fun _operatingSystems(): JsonField> = operatingSystems + + /** + * Returns the raw JSON value of [screen]. + * + * Unlike [screen], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("screen") @ExcludeMissing fun _screen(): JsonField = screen + + @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 [Fingerprint]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Fingerprint]. */ + class Builder internal constructor() { + + private var browsers: JsonField>? = null + private var devices: JsonField>? = null + private var httpVersion: JsonField = JsonMissing.of() + private var locales: JsonField>? = null + private var operatingSystems: JsonField>? = null + private var screen: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(fingerprint: Fingerprint) = apply { + browsers = fingerprint.browsers.map { it.toMutableList() } + devices = fingerprint.devices.map { it.toMutableList() } + httpVersion = fingerprint.httpVersion + locales = fingerprint.locales.map { it.toMutableList() } + operatingSystems = fingerprint.operatingSystems.map { it.toMutableList() } + screen = fingerprint.screen + additionalProperties = fingerprint.additionalProperties.toMutableMap() + } + + fun browsers(browsers: List) = browsers(JsonField.of(browsers)) + + /** + * Sets [Builder.browsers] to an arbitrary JSON value. + * + * You should usually call [Builder.browsers] with a well-typed `List` + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun browsers(browsers: JsonField>) = apply { + this.browsers = browsers.map { it.toMutableList() } + } + + /** + * Adds a single [Browser] to [browsers]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addBrowser(browser: Browser) = apply { + browsers = + (browsers ?: JsonField.of(mutableListOf())).also { + checkKnown("browsers", it).add(browser) + } + } + + fun devices(devices: List) = devices(JsonField.of(devices)) + + /** + * Sets [Builder.devices] to an arbitrary JSON value. + * + * You should usually call [Builder.devices] with a well-typed `List` + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun devices(devices: JsonField>) = apply { + this.devices = devices.map { it.toMutableList() } + } + + /** + * Adds a single [Device] to [devices]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addDevice(device: Device) = apply { + devices = + (devices ?: JsonField.of(mutableListOf())).also { + checkKnown("devices", it).add(device) + } + } + + fun httpVersion(httpVersion: HttpVersion) = + httpVersion(JsonField.of(httpVersion)) + + /** + * Sets [Builder.httpVersion] to an arbitrary JSON value. + * + * You should usually call [Builder.httpVersion] with a well-typed [HttpVersion] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun httpVersion(httpVersion: JsonField) = apply { + this.httpVersion = httpVersion + } + + fun locales(locales: List) = locales(JsonField.of(locales)) + + /** + * Sets [Builder.locales] to an arbitrary JSON value. + * + * You should usually call [Builder.locales] with a well-typed `List` + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun locales(locales: JsonField>) = apply { + this.locales = locales.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [locales]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addLocale(locale: String) = apply { + locales = + (locales ?: JsonField.of(mutableListOf())).also { + checkKnown("locales", it).add(locale) + } + } + + fun operatingSystems(operatingSystems: List) = + operatingSystems(JsonField.of(operatingSystems)) + + /** + * Sets [Builder.operatingSystems] to an arbitrary JSON value. + * + * You should usually call [Builder.operatingSystems] with a well-typed + * `List` value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun operatingSystems(operatingSystems: JsonField>) = + apply { + this.operatingSystems = operatingSystems.map { it.toMutableList() } + } + + /** + * Adds a single [OperatingSystem] to [operatingSystems]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addOperatingSystem(operatingSystem: OperatingSystem) = apply { + operatingSystems = + (operatingSystems ?: JsonField.of(mutableListOf())).also { + checkKnown("operatingSystems", it).add(operatingSystem) + } + } + + fun screen(screen: Screen) = screen(JsonField.of(screen)) + + /** + * Sets [Builder.screen] to an arbitrary JSON value. + * + * You should usually call [Builder.screen] with a well-typed [Screen] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun screen(screen: JsonField) = apply { this.screen = screen } + + 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 [Fingerprint]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Fingerprint = + Fingerprint( + (browsers ?: JsonMissing.of()).map { it.toImmutable() }, + (devices ?: JsonMissing.of()).map { it.toImmutable() }, + httpVersion, + (locales ?: JsonMissing.of()).map { it.toImmutable() }, + (operatingSystems ?: JsonMissing.of()).map { it.toImmutable() }, + screen, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Fingerprint = apply { + if (validated) { + return@apply + } + + browsers().ifPresent { it.forEach { it.validate() } } + devices().ifPresent { it.forEach { it.validate() } } + httpVersion().ifPresent { it.validate() } + locales() + operatingSystems().ifPresent { it.forEach { it.validate() } } + screen().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 = + (browsers.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (devices.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (httpVersion.asKnown().getOrNull()?.validity() ?: 0) + + (locales.asKnown().getOrNull()?.size ?: 0) + + (operatingSystems.asKnown().getOrNull()?.sumOf { it.validity().toInt() } + ?: 0) + + (screen.asKnown().getOrNull()?.validity() ?: 0) + + class Browser + @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 CHROME = of("chrome") + + @JvmField val EDGE = of("edge") + + @JvmField val FIREFOX = of("firefox") + + @JvmField val SAFARI = of("safari") + + @JvmStatic fun of(value: String) = Browser(JsonField.of(value)) + } + + /** An enum containing [Browser]'s known values. */ + enum class Known { + CHROME, + EDGE, + FIREFOX, + SAFARI, + } + + /** + * An enum containing [Browser]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Browser] 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 { + CHROME, + EDGE, + FIREFOX, + SAFARI, + /** + * An enum member indicating that [Browser] 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) { + CHROME -> Value.CHROME + EDGE -> Value.EDGE + FIREFOX -> Value.FIREFOX + SAFARI -> Value.SAFARI + 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) { + CHROME -> Known.CHROME + EDGE -> Known.EDGE + FIREFOX -> Known.FIREFOX + SAFARI -> Known.SAFARI + else -> throw StagehandInvalidDataException("Unknown Browser: $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 + + fun validate(): Browser = 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 Browser && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Device + @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 DESKTOP = of("desktop") + + @JvmField val MOBILE = of("mobile") + + @JvmStatic fun of(value: String) = Device(JsonField.of(value)) + } + + /** An enum containing [Device]'s known values. */ + enum class Known { + DESKTOP, + MOBILE, + } + + /** + * An enum containing [Device]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Device] 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 { + DESKTOP, + MOBILE, + /** + * An enum member indicating that [Device] 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) { + DESKTOP -> Value.DESKTOP + MOBILE -> Value.MOBILE + 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) { + DESKTOP -> Known.DESKTOP + MOBILE -> Known.MOBILE + else -> throw StagehandInvalidDataException("Unknown Device: $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 + + fun validate(): Device = 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 Device && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class HttpVersion + @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 _1 = of("1") + + @JvmField val _2 = of("2") + + @JvmStatic fun of(value: String) = HttpVersion(JsonField.of(value)) + } + + /** An enum containing [HttpVersion]'s known values. */ + enum class Known { + _1, + _2, + } + + /** + * An enum containing [HttpVersion]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [HttpVersion] 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 { + _1, + _2, + /** + * An enum member indicating that [HttpVersion] 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) { + _1 -> Value._1 + _2 -> Value._2 + 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) { + _1 -> Known._1 + _2 -> Known._2 + else -> + throw StagehandInvalidDataException("Unknown HttpVersion: $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 + + fun validate(): HttpVersion = 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 HttpVersion && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class OperatingSystem + @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 ANDROID = of("android") + + @JvmField val IOS = of("ios") + + @JvmField val LINUX = of("linux") + + @JvmField val MACOS = of("macos") + + @JvmField val WINDOWS = of("windows") + + @JvmStatic fun of(value: String) = OperatingSystem(JsonField.of(value)) + } + + /** An enum containing [OperatingSystem]'s known values. */ + enum class Known { + ANDROID, + IOS, + LINUX, + MACOS, + WINDOWS, + } + + /** + * An enum containing [OperatingSystem]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [OperatingSystem] 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 { + ANDROID, + IOS, + LINUX, + MACOS, + WINDOWS, + /** + * An enum member indicating that [OperatingSystem] 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) { + ANDROID -> Value.ANDROID + IOS -> Value.IOS + LINUX -> Value.LINUX + MACOS -> Value.MACOS + WINDOWS -> Value.WINDOWS + 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) { + ANDROID -> Known.ANDROID + IOS -> Known.IOS + LINUX -> Known.LINUX + MACOS -> Known.MACOS + WINDOWS -> Known.WINDOWS + else -> + throw StagehandInvalidDataException( + "Unknown OperatingSystem: $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 + + fun validate(): OperatingSystem = 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 OperatingSystem && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Screen + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val maxHeight: JsonField, + private val maxWidth: JsonField, + private val minHeight: JsonField, + private val minWidth: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("maxHeight") + @ExcludeMissing + maxHeight: JsonField = JsonMissing.of(), + @JsonProperty("maxWidth") + @ExcludeMissing + maxWidth: JsonField = JsonMissing.of(), + @JsonProperty("minHeight") + @ExcludeMissing + minHeight: JsonField = JsonMissing.of(), + @JsonProperty("minWidth") + @ExcludeMissing + minWidth: JsonField = JsonMissing.of(), + ) : this(maxHeight, maxWidth, minHeight, minWidth, mutableMapOf()) + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun maxHeight(): Optional = maxHeight.getOptional("maxHeight") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun maxWidth(): Optional = maxWidth.getOptional("maxWidth") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun minHeight(): Optional = minHeight.getOptional("minHeight") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun minWidth(): Optional = minWidth.getOptional("minWidth") + + /** + * Returns the raw JSON value of [maxHeight]. + * + * Unlike [maxHeight], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("maxHeight") + @ExcludeMissing + fun _maxHeight(): JsonField = maxHeight + + /** + * Returns the raw JSON value of [maxWidth]. + * + * Unlike [maxWidth], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("maxWidth") + @ExcludeMissing + fun _maxWidth(): JsonField = maxWidth + + /** + * Returns the raw JSON value of [minHeight]. + * + * Unlike [minHeight], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("minHeight") + @ExcludeMissing + fun _minHeight(): JsonField = minHeight + + /** + * Returns the raw JSON value of [minWidth]. + * + * Unlike [minWidth], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("minWidth") + @ExcludeMissing + fun _minWidth(): JsonField = minWidth + + @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 [Screen]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Screen]. */ + class Builder internal constructor() { + + private var maxHeight: JsonField = JsonMissing.of() + private var maxWidth: JsonField = JsonMissing.of() + private var minHeight: JsonField = JsonMissing.of() + private var minWidth: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(screen: Screen) = apply { + maxHeight = screen.maxHeight + maxWidth = screen.maxWidth + minHeight = screen.minHeight + minWidth = screen.minWidth + additionalProperties = screen.additionalProperties.toMutableMap() + } + + fun maxHeight(maxHeight: Double) = maxHeight(JsonField.of(maxHeight)) + + /** + * Sets [Builder.maxHeight] to an arbitrary JSON value. + * + * You should usually call [Builder.maxHeight] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun maxHeight(maxHeight: JsonField) = apply { + this.maxHeight = maxHeight + } + + fun maxWidth(maxWidth: Double) = maxWidth(JsonField.of(maxWidth)) + + /** + * Sets [Builder.maxWidth] to an arbitrary JSON value. + * + * You should usually call [Builder.maxWidth] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun maxWidth(maxWidth: JsonField) = apply { + this.maxWidth = maxWidth + } + + fun minHeight(minHeight: Double) = minHeight(JsonField.of(minHeight)) + + /** + * Sets [Builder.minHeight] to an arbitrary JSON value. + * + * You should usually call [Builder.minHeight] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun minHeight(minHeight: JsonField) = apply { + this.minHeight = minHeight + } + + fun minWidth(minWidth: Double) = minWidth(JsonField.of(minWidth)) + + /** + * Sets [Builder.minWidth] to an arbitrary JSON value. + * + * You should usually call [Builder.minWidth] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun minWidth(minWidth: JsonField) = apply { + this.minWidth = minWidth + } + + 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 [Screen]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Screen = + Screen( + maxHeight, + maxWidth, + minHeight, + minWidth, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Screen = apply { + if (validated) { + return@apply + } + + maxHeight() + maxWidth() + minHeight() + minWidth() + 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 (maxHeight.asKnown().isPresent) 1 else 0) + + (if (maxWidth.asKnown().isPresent) 1 else 0) + + (if (minHeight.asKnown().isPresent) 1 else 0) + + (if (minWidth.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Screen && + maxHeight == other.maxHeight && + maxWidth == other.maxWidth && + minHeight == other.minHeight && + minWidth == other.minWidth && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(maxHeight, maxWidth, minHeight, minWidth, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Screen{maxHeight=$maxHeight, maxWidth=$maxWidth, minHeight=$minHeight, minWidth=$minWidth, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Fingerprint && + browsers == other.browsers && + devices == other.devices && + httpVersion == other.httpVersion && + locales == other.locales && + operatingSystems == other.operatingSystems && + screen == other.screen && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + browsers, + devices, + httpVersion, + locales, + operatingSystems, + screen, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Fingerprint{browsers=$browsers, devices=$devices, httpVersion=$httpVersion, locales=$locales, operatingSystems=$operatingSystems, screen=$screen, additionalProperties=$additionalProperties}" + } + + class Viewport + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val height: JsonField, + private val width: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("height") + @ExcludeMissing + height: JsonField = JsonMissing.of(), + @JsonProperty("width") + @ExcludeMissing + width: JsonField = JsonMissing.of(), + ) : this(height, width, mutableMapOf()) + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun height(): Optional = height.getOptional("height") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun width(): Optional = width.getOptional("width") + + /** + * Returns the raw JSON value of [height]. + * + * Unlike [height], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("height") @ExcludeMissing fun _height(): JsonField = height + + /** + * Returns the raw JSON value of [width]. + * + * Unlike [width], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("width") @ExcludeMissing fun _width(): JsonField = width + + @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 [Viewport]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Viewport]. */ + class Builder internal constructor() { + + private var height: JsonField = JsonMissing.of() + private var width: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(viewport: Viewport) = apply { + height = viewport.height + width = viewport.width + additionalProperties = viewport.additionalProperties.toMutableMap() + } + + fun height(height: Double) = height(JsonField.of(height)) + + /** + * Sets [Builder.height] to an arbitrary JSON value. + * + * You should usually call [Builder.height] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun height(height: JsonField) = apply { this.height = height } + + fun width(width: Double) = width(JsonField.of(width)) + + /** + * Sets [Builder.width] to an arbitrary JSON value. + * + * You should usually call [Builder.width] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun width(width: JsonField) = apply { this.width = width } + + 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 [Viewport]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Viewport = + Viewport(height, width, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Viewport = apply { + if (validated) { + return@apply + } + + height() + width() + 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 (height.asKnown().isPresent) 1 else 0) + + (if (width.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Viewport && + height == other.height && + width == other.width && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(height, width, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Viewport{height=$height, width=$width, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BrowserSettings && + advancedStealth == other.advancedStealth && + blockAds == other.blockAds && + context == other.context && + extensionId == other.extensionId && + fingerprint == other.fingerprint && + logSession == other.logSession && + recordSession == other.recordSession && + solveCaptchas == other.solveCaptchas && + viewport == other.viewport && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + advancedStealth, + blockAds, + context, + extensionId, + fingerprint, + logSession, + recordSession, + solveCaptchas, + viewport, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BrowserSettings{advancedStealth=$advancedStealth, blockAds=$blockAds, context=$context, extensionId=$extensionId, fingerprint=$fingerprint, logSession=$logSession, recordSession=$recordSession, solveCaptchas=$solveCaptchas, viewport=$viewport, additionalProperties=$additionalProperties}" + } + + @JsonDeserialize(using = Proxies.Deserializer::class) + @JsonSerialize(using = Proxies.Serializer::class) + class Proxies + private constructor( + private val bool: Boolean? = null, + private val proxyConfigList: List? = null, + private val _json: JsonValue? = null, + ) { + + fun bool(): Optional = Optional.ofNullable(bool) + + fun proxyConfigList(): Optional> = + Optional.ofNullable(proxyConfigList) + + fun isBool(): Boolean = bool != null + + fun isProxyConfigList(): Boolean = proxyConfigList != null + + fun asBool(): Boolean = bool.getOrThrow("bool") + + fun asProxyConfigList(): List = + proxyConfigList.getOrThrow("proxyConfigList") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + bool != null -> visitor.visitBool(bool) + proxyConfigList != null -> visitor.visitProxyConfigList(proxyConfigList) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Proxies = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitBool(bool: Boolean) {} + + override fun visitProxyConfigList(proxyConfigList: List) { + proxyConfigList.forEach { 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 = + accept( + object : Visitor { + override fun visitBool(bool: Boolean) = 1 + + override fun visitProxyConfigList(proxyConfigList: List) = + proxyConfigList.sumOf { it.validity().toInt() } + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Proxies && + bool == other.bool && + proxyConfigList == other.proxyConfigList + } + + override fun hashCode(): Int = Objects.hash(bool, proxyConfigList) + + override fun toString(): String = + when { + bool != null -> "Proxies{bool=$bool}" + proxyConfigList != null -> "Proxies{proxyConfigList=$proxyConfigList}" + _json != null -> "Proxies{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Proxies") + } + + companion object { + + @JvmStatic fun ofBool(bool: Boolean) = Proxies(bool = bool) + + @JvmStatic + fun ofProxyConfigList(proxyConfigList: List) = + Proxies(proxyConfigList = proxyConfigList.toImmutable()) + } + + /** + * An interface that defines how to map each variant of [Proxies] to a value of type + * [T]. + */ + interface Visitor { + + fun visitBool(bool: Boolean): T + + fun visitProxyConfigList(proxyConfigList: List): T + + /** + * Maps an unknown variant of [Proxies] to a value of type [T]. + * + * An instance of [Proxies] 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 Proxies: $json") + } + } + + internal class Deserializer : BaseDeserializer(Proxies::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Proxies { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Proxies(bool = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>())?.let { + Proxies(proxyConfigList = 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 string). + 0 -> Proxies(_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(Proxies::class) { + + override fun serialize( + value: Proxies, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.bool != null -> generator.writeObject(value.bool) + value.proxyConfigList != null -> + generator.writeObject(value.proxyConfigList) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Proxies") + } + } + } + + @JsonDeserialize(using = ProxyConfig.Deserializer::class) + @JsonSerialize(using = ProxyConfig.Serializer::class) + class ProxyConfig + private constructor( + private val browserbase: Browserbase? = null, + private val external: External? = null, + private val _json: JsonValue? = null, + ) { + + fun browserbase(): Optional = Optional.ofNullable(browserbase) + + fun external(): Optional = Optional.ofNullable(external) + + fun isBrowserbase(): Boolean = browserbase != null + + fun isExternal(): Boolean = external != null + + fun asBrowserbase(): Browserbase = browserbase.getOrThrow("browserbase") + + fun asExternal(): External = external.getOrThrow("external") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + browserbase != null -> visitor.visitBrowserbase(browserbase) + external != null -> visitor.visitExternal(external) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): ProxyConfig = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitBrowserbase(browserbase: Browserbase) { + browserbase.validate() + } + + override fun visitExternal(external: External) { + external.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 = + accept( + object : Visitor { + override fun visitBrowserbase(browserbase: Browserbase) = + browserbase.validity() + + override fun visitExternal(external: External) = external.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ProxyConfig && + browserbase == other.browserbase && + external == other.external + } + + override fun hashCode(): Int = Objects.hash(browserbase, external) + + override fun toString(): String = + when { + browserbase != null -> "ProxyConfig{browserbase=$browserbase}" + external != null -> "ProxyConfig{external=$external}" + _json != null -> "ProxyConfig{_unknown=$_json}" + else -> throw IllegalStateException("Invalid ProxyConfig") + } + + companion object { + + @JvmStatic + fun ofBrowserbase(browserbase: Browserbase) = + ProxyConfig(browserbase = browserbase) + + @JvmStatic fun ofExternal(external: External) = ProxyConfig(external = external) + } + + /** + * An interface that defines how to map each variant of [ProxyConfig] to a value of + * type [T]. + */ + interface Visitor { + + fun visitBrowserbase(browserbase: Browserbase): T + + fun visitExternal(external: External): T + + /** + * Maps an unknown variant of [ProxyConfig] to a value of type [T]. + * + * An instance of [ProxyConfig] 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 ProxyConfig: $json") + } + } + + internal class Deserializer : BaseDeserializer(ProxyConfig::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): ProxyConfig { + val json = JsonValue.fromJsonNode(node) + val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() + + when (type) { + "browserbase" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ProxyConfig(browserbase = it, _json = json) + } ?: ProxyConfig(_json = json) + } + "external" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ProxyConfig(external = it, _json = json) + } ?: ProxyConfig(_json = json) + } + } + + return ProxyConfig(_json = json) + } + } + + internal class Serializer : BaseSerializer(ProxyConfig::class) { + + override fun serialize( + value: ProxyConfig, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.browserbase != null -> generator.writeObject(value.browserbase) + value.external != null -> generator.writeObject(value.external) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid ProxyConfig") + } + } + } + + class Browserbase + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val type: JsonValue, + private val domainPattern: JsonField, + private val geolocation: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("domainPattern") + @ExcludeMissing + domainPattern: JsonField = JsonMissing.of(), + @JsonProperty("geolocation") + @ExcludeMissing + geolocation: JsonField = JsonMissing.of(), + ) : this(type, domainPattern, geolocation, mutableMapOf()) + + /** + * Expected to always return the following: + * ```java + * JsonValue.from("browserbase") + * ``` + * + * 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 + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun domainPattern(): Optional = + domainPattern.getOptional("domainPattern") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun geolocation(): Optional = + geolocation.getOptional("geolocation") + + /** + * Returns the raw JSON value of [domainPattern]. + * + * Unlike [domainPattern], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("domainPattern") + @ExcludeMissing + fun _domainPattern(): JsonField = domainPattern + + /** + * Returns the raw JSON value of [geolocation]. + * + * Unlike [geolocation], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("geolocation") + @ExcludeMissing + fun _geolocation(): JsonField = geolocation + + @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 [Browserbase]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Browserbase]. */ + class Builder internal constructor() { + + private var type: JsonValue = JsonValue.from("browserbase") + private var domainPattern: JsonField = JsonMissing.of() + private var geolocation: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(browserbase: Browserbase) = apply { + type = browserbase.type + domainPattern = browserbase.domainPattern + geolocation = browserbase.geolocation + additionalProperties = browserbase.additionalProperties.toMutableMap() + } + + /** + * 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("browserbase") + * ``` + * + * This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + fun domainPattern(domainPattern: String) = + domainPattern(JsonField.of(domainPattern)) + + /** + * Sets [Builder.domainPattern] to an arbitrary JSON value. + * + * You should usually call [Builder.domainPattern] with a well-typed + * [String] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun domainPattern(domainPattern: JsonField) = apply { + this.domainPattern = domainPattern + } + + fun geolocation(geolocation: Geolocation) = + geolocation(JsonField.of(geolocation)) + + /** + * Sets [Builder.geolocation] to an arbitrary JSON value. + * + * You should usually call [Builder.geolocation] with a well-typed + * [Geolocation] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun geolocation(geolocation: JsonField) = apply { + this.geolocation = geolocation + } + + 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 [Browserbase]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Browserbase = + Browserbase( + type, + domainPattern, + geolocation, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Browserbase = apply { + if (validated) { + return@apply + } + + _type().let { + if (it != JsonValue.from("browserbase")) { + throw StagehandInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + domainPattern() + geolocation().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 = + type.let { if (it == JsonValue.from("browserbase")) 1 else 0 } + + (if (domainPattern.asKnown().isPresent) 1 else 0) + + (geolocation.asKnown().getOrNull()?.validity() ?: 0) + + class Geolocation + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val city: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("city") + @ExcludeMissing + city: JsonField = JsonMissing.of(), + @JsonProperty("state") + @ExcludeMissing + state: JsonField = JsonMissing.of(), + ) : this(country, city, state, 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 country(): String = country.getRequired("country") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun city(): Optional = city.getOptional("city") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun state(): Optional = state.getOptional("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("country") + @ExcludeMissing + fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("state") + @ExcludeMissing + fun _state(): JsonField = state + + @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 + * [Geolocation]. + * + * The following fields are required: + * ```java + * .country() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Geolocation]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(geolocation: Geolocation) = apply { + country = geolocation.country + city = geolocation.city + state = geolocation.state + additionalProperties = + geolocation.additionalProperties.toMutableMap() + } + + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun country(country: JsonField) = apply { + this.country = country + } + + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + 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 [Geolocation]. + * + * Further updates to this [Builder] will not mutate the returned + * instance. + * + * The following fields are required: + * ```java + * .country() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Geolocation = + Geolocation( + checkRequired("country", country), + city, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Geolocation = apply { + if (validated) { + return@apply + } + + country() + city() + state() + 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 (country.asKnown().isPresent) 1 else 0) + + (if (city.asKnown().isPresent) 1 else 0) + + (if (state.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Geolocation && + country == other.country && + city == other.city && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(country, city, state, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Geolocation{country=$country, city=$city, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Browserbase && + type == other.type && + domainPattern == other.domainPattern && + geolocation == other.geolocation && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(type, domainPattern, geolocation, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Browserbase{type=$type, domainPattern=$domainPattern, geolocation=$geolocation, additionalProperties=$additionalProperties}" + } + + class External + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val server: JsonField, + private val type: JsonValue, + private val domainPattern: JsonField, + private val password: JsonField, + private val username: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("server") + @ExcludeMissing + server: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("domainPattern") + @ExcludeMissing + domainPattern: JsonField = JsonMissing.of(), + @JsonProperty("password") + @ExcludeMissing + password: JsonField = JsonMissing.of(), + @JsonProperty("username") + @ExcludeMissing + username: JsonField = JsonMissing.of(), + ) : this(server, type, domainPattern, password, username, 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 server(): String = server.getRequired("server") + + /** + * Expected to always return the following: + * ```java + * JsonValue.from("external") + * ``` + * + * 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 + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun domainPattern(): Optional = + domainPattern.getOptional("domainPattern") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun password(): Optional = password.getOptional("password") + + /** + * @throws StagehandInvalidDataException if the JSON field has an unexpected + * type (e.g. if the server responded with an unexpected value). + */ + fun username(): Optional = username.getOptional("username") + + /** + * Returns the raw JSON value of [server]. + * + * Unlike [server], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("server") + @ExcludeMissing + fun _server(): JsonField = server + + /** + * Returns the raw JSON value of [domainPattern]. + * + * Unlike [domainPattern], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("domainPattern") + @ExcludeMissing + fun _domainPattern(): JsonField = domainPattern + + /** + * Returns the raw JSON value of [password]. + * + * Unlike [password], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("password") + @ExcludeMissing + fun _password(): JsonField = password + + /** + * Returns the raw JSON value of [username]. + * + * Unlike [username], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("username") + @ExcludeMissing + fun _username(): JsonField = username + + @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 [External]. + * + * The following fields are required: + * ```java + * .server() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [External]. */ + class Builder internal constructor() { + + private var server: JsonField? = null + private var type: JsonValue = JsonValue.from("external") + private var domainPattern: JsonField = JsonMissing.of() + private var password: JsonField = JsonMissing.of() + private var username: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(external: External) = apply { + server = external.server + type = external.type + domainPattern = external.domainPattern + password = external.password + username = external.username + additionalProperties = external.additionalProperties.toMutableMap() + } + + fun server(server: String) = server(JsonField.of(server)) + + /** + * Sets [Builder.server] to an arbitrary JSON value. + * + * You should usually call [Builder.server] with a well-typed [String] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun server(server: JsonField) = apply { this.server = server } + + /** + * 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("external") + * ``` + * + * This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + fun domainPattern(domainPattern: String) = + domainPattern(JsonField.of(domainPattern)) + + /** + * Sets [Builder.domainPattern] to an arbitrary JSON value. + * + * You should usually call [Builder.domainPattern] with a well-typed + * [String] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun domainPattern(domainPattern: JsonField) = apply { + this.domainPattern = domainPattern + } + + fun password(password: String) = password(JsonField.of(password)) + + /** + * Sets [Builder.password] to an arbitrary JSON value. + * + * You should usually call [Builder.password] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun password(password: JsonField) = apply { + this.password = password + } + + fun username(username: String) = username(JsonField.of(username)) + + /** + * Sets [Builder.username] to an arbitrary JSON value. + * + * You should usually call [Builder.username] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun username(username: JsonField) = apply { + this.username = username + } + + 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 [External]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .server() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): External = + External( + checkRequired("server", server), + type, + domainPattern, + password, + username, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): External = apply { + if (validated) { + return@apply + } + + server() + _type().let { + if (it != JsonValue.from("external")) { + throw StagehandInvalidDataException( + "'type' is invalid, received $it" + ) + } + } + domainPattern() + password() + username() + 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 (server.asKnown().isPresent) 1 else 0) + + type.let { if (it == JsonValue.from("external")) 1 else 0 } + + (if (domainPattern.asKnown().isPresent) 1 else 0) + + (if (password.asKnown().isPresent) 1 else 0) + + (if (username.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is External && + server == other.server && + type == other.type && + domainPattern == other.domainPattern && + password == other.password && + username == other.username && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + server, + type, + domainPattern, + password, + username, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "External{server=$server, type=$type, domainPattern=$domainPattern, password=$password, username=$username, additionalProperties=$additionalProperties}" + } + } + } + + class Region @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 US_WEST_2 = of("us-west-2") + + @JvmField val US_EAST_1 = of("us-east-1") + + @JvmField val EU_CENTRAL_1 = of("eu-central-1") + + @JvmField val AP_SOUTHEAST_1 = of("ap-southeast-1") + + @JvmStatic fun of(value: String) = Region(JsonField.of(value)) + } + + /** An enum containing [Region]'s known values. */ + enum class Known { + US_WEST_2, + US_EAST_1, + EU_CENTRAL_1, + AP_SOUTHEAST_1, + } + + /** + * An enum containing [Region]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Region] 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 { + US_WEST_2, + US_EAST_1, + EU_CENTRAL_1, + AP_SOUTHEAST_1, + /** + * An enum member indicating that [Region] 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) { + US_WEST_2 -> Value.US_WEST_2 + US_EAST_1 -> Value.US_EAST_1 + EU_CENTRAL_1 -> Value.EU_CENTRAL_1 + AP_SOUTHEAST_1 -> Value.AP_SOUTHEAST_1 + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws StagehandInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + US_WEST_2 -> Known.US_WEST_2 + US_EAST_1 -> Known.US_EAST_1 + EU_CENTRAL_1 -> Known.EU_CENTRAL_1 + AP_SOUTHEAST_1 -> Known.AP_SOUTHEAST_1 + else -> throw StagehandInvalidDataException("Unknown Region: $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 + + fun validate(): Region = 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 Region && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class UserMetadata + @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 [UserMetadata]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UserMetadata]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(userMetadata: UserMetadata) = apply { + additionalProperties = userMetadata.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 [UserMetadata]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): UserMetadata = UserMetadata(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): UserMetadata = 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 UserMetadata && additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "UserMetadata{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BrowserbaseSessionCreateParams && + browserSettings == other.browserSettings && + extensionId == other.extensionId && + keepAlive == other.keepAlive && + projectId == other.projectId && + proxies == other.proxies && + region == other.region && + timeout == other.timeout && + userMetadata == other.userMetadata && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + browserSettings, + extensionId, + keepAlive, + projectId, + proxies, + region, + timeout, + userMetadata, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BrowserbaseSessionCreateParams{browserSettings=$browserSettings, extensionId=$extensionId, keepAlive=$keepAlive, projectId=$projectId, proxies=$proxies, region=$region, timeout=$timeout, userMetadata=$userMetadata, additionalProperties=$additionalProperties}" + } + + /** Client SDK language */ + class XLanguage @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 TYPESCRIPT = of("typescript") + + @JvmField val PYTHON = of("python") + + @JvmField val PLAYGROUND = of("playground") + + @JvmStatic fun of(value: String) = XLanguage(JsonField.of(value)) + } + + /** An enum containing [XLanguage]'s known values. */ + enum class Known { + TYPESCRIPT, + PYTHON, + PLAYGROUND, + } + + /** + * An enum containing [XLanguage]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [XLanguage] 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 { + TYPESCRIPT, + PYTHON, + PLAYGROUND, + /** + * An enum member indicating that [XLanguage] 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) { + TYPESCRIPT -> Value.TYPESCRIPT + PYTHON -> Value.PYTHON + PLAYGROUND -> Value.PLAYGROUND + 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) { + TYPESCRIPT -> Known.TYPESCRIPT + PYTHON -> Known.PYTHON + PLAYGROUND -> Known.PLAYGROUND + else -> throw StagehandInvalidDataException("Unknown XLanguage: $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 + + fun validate(): XLanguage = 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 XLanguage && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Whether to stream the response via SSE */ + class XStreamResponse @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 TRUE = of("true") + + @JvmField val FALSE = of("false") + + @JvmStatic fun of(value: String) = XStreamResponse(JsonField.of(value)) + } + + /** An enum containing [XStreamResponse]'s known values. */ + enum class Known { + TRUE, + FALSE, + } + + /** + * An enum containing [XStreamResponse]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [XStreamResponse] 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 { + TRUE, + FALSE, + /** + * An enum member indicating that [XStreamResponse] 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) { + TRUE -> Value.TRUE + FALSE -> Value.FALSE + 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) { + TRUE -> Known.TRUE + FALSE -> Known.FALSE + else -> throw StagehandInvalidDataException("Unknown XStreamResponse: $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 + + fun validate(): XStreamResponse = 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 XStreamResponse && 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 SessionStartParams && + xLanguage == other.xLanguage && + xSdkVersion == other.xSdkVersion && + xSentAt == other.xSentAt && + xStreamResponse == other.xStreamResponse && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash( + xLanguage, + xSdkVersion, + xSentAt, + xStreamResponse, + body, + additionalHeaders, + additionalQueryParams, + ) override fun toString() = - "SessionStartParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" + "SessionStartParams{xLanguage=$xLanguage, xSdkVersion=$xSdkVersion, xSentAt=$xSentAt, xStreamResponse=$xStreamResponse, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" } diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartResponse.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartResponse.kt index 2e71372..13ed630 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartResponse.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartResponse.kt @@ -14,50 +14,49 @@ import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty import java.util.Collections import java.util.Objects +import kotlin.jvm.optionals.getOrNull class SessionStartResponse @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val available: JsonField, - private val sessionId: JsonField, + private val data: JsonField, + private val success: JsonField, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("available") @ExcludeMissing available: JsonField = JsonMissing.of(), - @JsonProperty("sessionId") @ExcludeMissing sessionId: JsonField = JsonMissing.of(), - ) : this(available, sessionId, mutableMapOf()) + @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(), + @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(), + ) : this(data, success, mutableMapOf()) /** - * Whether the session is ready to use - * * @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 available(): Boolean = available.getRequired("available") + fun data(): Data = data.getRequired("data") /** - * Unique identifier for the session + * Indicates whether the request was successful * * @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 sessionId(): String = sessionId.getRequired("sessionId") + fun success(): Boolean = success.getRequired("success") /** - * Returns the raw JSON value of [available]. + * Returns the raw JSON value of [data]. * - * Unlike [available], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("available") @ExcludeMissing fun _available(): JsonField = available + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField = data /** - * Returns the raw JSON value of [sessionId]. + * Returns the raw JSON value of [success]. * - * Unlike [sessionId], this method doesn't throw if the JSON field has an unexpected type. + * Unlike [success], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("sessionId") @ExcludeMissing fun _sessionId(): JsonField = sessionId + @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -78,8 +77,8 @@ private constructor( * * The following fields are required: * ```java - * .available() - * .sessionId() + * .data() + * .success() * ``` */ @JvmStatic fun builder() = Builder() @@ -88,40 +87,37 @@ private constructor( /** A builder for [SessionStartResponse]. */ class Builder internal constructor() { - private var available: JsonField? = null - private var sessionId: JsonField? = null + private var data: JsonField? = null + private var success: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(sessionStartResponse: SessionStartResponse) = apply { - available = sessionStartResponse.available - sessionId = sessionStartResponse.sessionId + data = sessionStartResponse.data + success = sessionStartResponse.success additionalProperties = sessionStartResponse.additionalProperties.toMutableMap() } - /** Whether the session is ready to use */ - fun available(available: Boolean) = available(JsonField.of(available)) + fun data(data: Data) = data(JsonField.of(data)) /** - * Sets [Builder.available] to an arbitrary JSON value. + * Sets [Builder.data] to an arbitrary JSON value. * - * You should usually call [Builder.available] with a well-typed [Boolean] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. + * You should usually call [Builder.data] with a well-typed [Data] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun available(available: JsonField) = apply { this.available = available } + fun data(data: JsonField) = apply { this.data = data } - /** Unique identifier for the session */ - fun sessionId(sessionId: String) = sessionId(JsonField.of(sessionId)) + /** Indicates whether the request was successful */ + fun success(success: Boolean) = success(JsonField.of(success)) /** - * Sets [Builder.sessionId] to an arbitrary JSON value. + * Sets [Builder.success] to an arbitrary JSON value. * - * You should usually call [Builder.sessionId] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. + * You should usually call [Builder.success] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun sessionId(sessionId: JsonField) = apply { this.sessionId = sessionId } + fun success(success: JsonField) = apply { this.success = success } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -149,16 +145,16 @@ private constructor( * * The following fields are required: * ```java - * .available() - * .sessionId() + * .data() + * .success() * ``` * * @throws IllegalStateException if any required field is unset. */ fun build(): SessionStartResponse = SessionStartResponse( - checkRequired("available", available), - checkRequired("sessionId", sessionId), + checkRequired("data", data), + checkRequired("success", success), additionalProperties.toMutableMap(), ) } @@ -170,8 +166,8 @@ private constructor( return@apply } - available() - sessionId() + data().validate() + success() validated = true } @@ -190,8 +186,249 @@ private constructor( */ @JvmSynthetic internal fun validity(): Int = - (if (available.asKnown().isPresent) 1 else 0) + - (if (sessionId.asKnown().isPresent) 1 else 0) + (data.asKnown().getOrNull()?.validity() ?: 0) + (if (success.asKnown().isPresent) 1 else 0) + + class Data + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val available: JsonField, + private val connectUrl: JsonField, + private val sessionId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("available") + @ExcludeMissing + available: JsonField = JsonMissing.of(), + @JsonProperty("connectUrl") + @ExcludeMissing + connectUrl: JsonField = JsonMissing.of(), + @JsonProperty("sessionId") + @ExcludeMissing + sessionId: JsonField = JsonMissing.of(), + ) : this(available, connectUrl, sessionId, 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 available(): Boolean = available.getRequired("available") + + /** + * CDP WebSocket URL for connecting to the Browserbase cloud browser + * + * @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 connectUrl(): String = connectUrl.getRequired("connectUrl") + + /** + * Unique Browserbase session identifier + * + * @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 sessionId(): String = sessionId.getRequired("sessionId") + + /** + * Returns the raw JSON value of [available]. + * + * Unlike [available], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("available") @ExcludeMissing fun _available(): JsonField = available + + /** + * Returns the raw JSON value of [connectUrl]. + * + * Unlike [connectUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("connectUrl") + @ExcludeMissing + fun _connectUrl(): JsonField = connectUrl + + /** + * Returns the raw JSON value of [sessionId]. + * + * Unlike [sessionId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sessionId") @ExcludeMissing fun _sessionId(): JsonField = sessionId + + @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 [Data]. + * + * The following fields are required: + * ```java + * .available() + * .connectUrl() + * .sessionId() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Data]. */ + class Builder internal constructor() { + + private var available: JsonField? = null + private var connectUrl: JsonField? = null + private var sessionId: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(data: Data) = apply { + available = data.available + connectUrl = data.connectUrl + sessionId = data.sessionId + additionalProperties = data.additionalProperties.toMutableMap() + } + + fun available(available: Boolean) = available(JsonField.of(available)) + + /** + * Sets [Builder.available] to an arbitrary JSON value. + * + * You should usually call [Builder.available] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun available(available: JsonField) = apply { this.available = available } + + /** CDP WebSocket URL for connecting to the Browserbase cloud browser */ + fun connectUrl(connectUrl: String) = connectUrl(JsonField.of(connectUrl)) + + /** + * Sets [Builder.connectUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.connectUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun connectUrl(connectUrl: JsonField) = apply { this.connectUrl = connectUrl } + + /** Unique Browserbase session identifier */ + fun sessionId(sessionId: String) = sessionId(JsonField.of(sessionId)) + + /** + * Sets [Builder.sessionId] to an arbitrary JSON value. + * + * You should usually call [Builder.sessionId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sessionId(sessionId: JsonField) = apply { this.sessionId = sessionId } + + 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 [Data]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .available() + * .connectUrl() + * .sessionId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Data = + Data( + checkRequired("available", available), + checkRequired("connectUrl", connectUrl), + checkRequired("sessionId", sessionId), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Data = apply { + if (validated) { + return@apply + } + + available() + connectUrl() + sessionId() + 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 (available.asKnown().isPresent) 1 else 0) + + (if (connectUrl.asKnown().isPresent) 1 else 0) + + (if (sessionId.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Data && + available == other.available && + connectUrl == other.connectUrl && + sessionId == other.sessionId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(available, connectUrl, sessionId, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Data{available=$available, connectUrl=$connectUrl, sessionId=$sessionId, additionalProperties=$additionalProperties}" + } override fun equals(other: Any?): Boolean { if (this === other) { @@ -199,15 +436,15 @@ private constructor( } return other is SessionStartResponse && - available == other.available && - sessionId == other.sessionId && + data == other.data && + success == other.success && additionalProperties == other.additionalProperties } - private val hashCode: Int by lazy { Objects.hash(available, sessionId, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(data, success, additionalProperties) } override fun hashCode(): Int = hashCode override fun toString() = - "SessionStartResponse{available=$available, sessionId=$sessionId, additionalProperties=$additionalProperties}" + "SessionStartResponse{data=$data, success=$success, additionalProperties=$additionalProperties}" } diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/StreamEvent.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/StreamEvent.kt new file mode 100644 index 0000000..d58dc3b --- /dev/null +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/StreamEvent.kt @@ -0,0 +1,1175 @@ +// File generated from our OpenAPI spec by Stainless. + +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.errors.StagehandInvalidDataException +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** Server-Sent Event emitted during streaming responses. Events are sent as `data: \n\n`. */ +class StreamEvent +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val id: JsonField, + private val data: JsonField, + private val type: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + ) : this(id, data, type, mutableMapOf()) + + /** + * Unique identifier for this event + * + * @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 id(): String = id.getRequired("id") + + /** + * @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 data(): Data = data.getRequired("data") + + /** + * Type of stream event - system events or log messages + * + * @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 type(): Type = type.getRequired("type") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField = data + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [StreamEvent]. + * + * The following fields are required: + * ```java + * .id() + * .data() + * .type() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [StreamEvent]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var data: JsonField? = null + private var type: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(streamEvent: StreamEvent) = apply { + id = streamEvent.id + data = streamEvent.data + type = streamEvent.type + additionalProperties = streamEvent.additionalProperties.toMutableMap() + } + + /** Unique identifier for this event */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + fun data(data: Data) = data(JsonField.of(data)) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed [Data] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun data(data: JsonField) = apply { this.data = data } + + /** + * Alias for calling [data] with + * `Data.ofStreamEventSystemDataOutput(streamEventSystemDataOutput)`. + */ + fun data(streamEventSystemDataOutput: Data.StreamEventSystemDataOutput) = + data(Data.ofStreamEventSystemDataOutput(streamEventSystemDataOutput)) + + /** + * Alias for calling [data] with + * `Data.ofStreamEventLogDataOutput(streamEventLogDataOutput)`. + */ + fun data(streamEventLogDataOutput: Data.StreamEventLogDataOutput) = + data(Data.ofStreamEventLogDataOutput(streamEventLogDataOutput)) + + /** Type of stream event - system events or log messages */ + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun type(type: JsonField) = apply { this.type = type } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [StreamEvent]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .id() + * .data() + * .type() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): StreamEvent = + StreamEvent( + checkRequired("id", id), + checkRequired("data", data), + checkRequired("type", type), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): StreamEvent = apply { + if (validated) { + return@apply + } + + id() + data().validate() + type().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 (id.asKnown().isPresent) 1 else 0) + + (data.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + @JsonDeserialize(using = Data.Deserializer::class) + @JsonSerialize(using = Data.Serializer::class) + class Data + private constructor( + private val streamEventSystemDataOutput: StreamEventSystemDataOutput? = null, + private val streamEventLogDataOutput: StreamEventLogDataOutput? = null, + private val _json: JsonValue? = null, + ) { + + fun streamEventSystemDataOutput(): Optional = + Optional.ofNullable(streamEventSystemDataOutput) + + fun streamEventLogDataOutput(): Optional = + Optional.ofNullable(streamEventLogDataOutput) + + fun isStreamEventSystemDataOutput(): Boolean = streamEventSystemDataOutput != null + + fun isStreamEventLogDataOutput(): Boolean = streamEventLogDataOutput != null + + fun asStreamEventSystemDataOutput(): StreamEventSystemDataOutput = + streamEventSystemDataOutput.getOrThrow("streamEventSystemDataOutput") + + fun asStreamEventLogDataOutput(): StreamEventLogDataOutput = + streamEventLogDataOutput.getOrThrow("streamEventLogDataOutput") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + streamEventSystemDataOutput != null -> + visitor.visitStreamEventSystemDataOutput(streamEventSystemDataOutput) + streamEventLogDataOutput != null -> + visitor.visitStreamEventLogDataOutput(streamEventLogDataOutput) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Data = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitStreamEventSystemDataOutput( + streamEventSystemDataOutput: StreamEventSystemDataOutput + ) { + streamEventSystemDataOutput.validate() + } + + override fun visitStreamEventLogDataOutput( + streamEventLogDataOutput: StreamEventLogDataOutput + ) { + streamEventLogDataOutput.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 = + accept( + object : Visitor { + override fun visitStreamEventSystemDataOutput( + streamEventSystemDataOutput: StreamEventSystemDataOutput + ) = streamEventSystemDataOutput.validity() + + override fun visitStreamEventLogDataOutput( + streamEventLogDataOutput: StreamEventLogDataOutput + ) = streamEventLogDataOutput.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Data && + streamEventSystemDataOutput == other.streamEventSystemDataOutput && + streamEventLogDataOutput == other.streamEventLogDataOutput + } + + override fun hashCode(): Int = + Objects.hash(streamEventSystemDataOutput, streamEventLogDataOutput) + + override fun toString(): String = + when { + streamEventSystemDataOutput != null -> + "Data{streamEventSystemDataOutput=$streamEventSystemDataOutput}" + streamEventLogDataOutput != null -> + "Data{streamEventLogDataOutput=$streamEventLogDataOutput}" + _json != null -> "Data{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Data") + } + + companion object { + + @JvmStatic + fun ofStreamEventSystemDataOutput( + streamEventSystemDataOutput: StreamEventSystemDataOutput + ) = Data(streamEventSystemDataOutput = streamEventSystemDataOutput) + + @JvmStatic + fun ofStreamEventLogDataOutput(streamEventLogDataOutput: StreamEventLogDataOutput) = + Data(streamEventLogDataOutput = streamEventLogDataOutput) + } + + /** An interface that defines how to map each variant of [Data] to a value of type [T]. */ + interface Visitor { + + fun visitStreamEventSystemDataOutput( + streamEventSystemDataOutput: StreamEventSystemDataOutput + ): T + + fun visitStreamEventLogDataOutput(streamEventLogDataOutput: StreamEventLogDataOutput): T + + /** + * Maps an unknown variant of [Data] to a value of type [T]. + * + * An instance of [Data] 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 Data: $json") + } + } + + internal class Deserializer : BaseDeserializer(Data::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Data { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef()) + ?.let { Data(streamEventSystemDataOutput = it, _json = json) }, + tryDeserialize(node, jacksonTypeRef())?.let { + Data(streamEventLogDataOutput = 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 -> Data(_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(Data::class) { + + override fun serialize( + value: Data, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.streamEventSystemDataOutput != null -> + generator.writeObject(value.streamEventSystemDataOutput) + value.streamEventLogDataOutput != null -> + generator.writeObject(value.streamEventLogDataOutput) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Data") + } + } + } + + class StreamEventSystemDataOutput + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val status: JsonField, + private val error: JsonField, + private val result: JsonValue, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("status") + @ExcludeMissing + status: JsonField = JsonMissing.of(), + @JsonProperty("error") @ExcludeMissing error: JsonField = JsonMissing.of(), + @JsonProperty("result") @ExcludeMissing result: JsonValue = JsonMissing.of(), + ) : this(status, error, result, mutableMapOf()) + + /** + * Current status of the streaming operation + * + * @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 status(): Status = status.getRequired("status") + + /** + * Error message (present when status is 'error') + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun error(): Optional = error.getOptional("error") + + /** Operation result (present when status is 'finished') */ + @JsonProperty("result") @ExcludeMissing fun _result(): JsonValue = result + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + /** + * Returns the raw JSON value of [error]. + * + * Unlike [error], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("error") @ExcludeMissing fun _error(): JsonField = error + + @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 + * [StreamEventSystemDataOutput]. + * + * The following fields are required: + * ```java + * .status() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [StreamEventSystemDataOutput]. */ + class Builder internal constructor() { + + private var status: JsonField? = null + private var error: JsonField = JsonMissing.of() + private var result: JsonValue = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(streamEventSystemDataOutput: StreamEventSystemDataOutput) = + apply { + status = streamEventSystemDataOutput.status + error = streamEventSystemDataOutput.error + result = streamEventSystemDataOutput.result + additionalProperties = + streamEventSystemDataOutput.additionalProperties.toMutableMap() + } + + /** Current status of the streaming operation */ + fun status(status: Status) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + /** Error message (present when status is 'error') */ + fun error(error: String) = error(JsonField.of(error)) + + /** + * Sets [Builder.error] to an arbitrary JSON value. + * + * You should usually call [Builder.error] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun error(error: JsonField) = apply { this.error = error } + + /** Operation result (present when status is 'finished') */ + fun result(result: JsonValue) = apply { this.result = result } + + 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 [StreamEventSystemDataOutput]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .status() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): StreamEventSystemDataOutput = + StreamEventSystemDataOutput( + checkRequired("status", status), + error, + result, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): StreamEventSystemDataOutput = apply { + if (validated) { + return@apply + } + + status().validate() + error() + 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 = + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (error.asKnown().isPresent) 1 else 0) + + /** Current status of the streaming operation */ + class Status @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 STARTING = of("starting") + + @JvmField val CONNECTED = of("connected") + + @JvmField val RUNNING = of("running") + + @JvmField val FINISHED = of("finished") + + @JvmField val ERROR = of("error") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + STARTING, + CONNECTED, + RUNNING, + FINISHED, + ERROR, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] 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 { + STARTING, + CONNECTED, + RUNNING, + FINISHED, + ERROR, + /** + * An enum member indicating that [Status] 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) { + STARTING -> Value.STARTING + CONNECTED -> Value.CONNECTED + RUNNING -> Value.RUNNING + FINISHED -> Value.FINISHED + ERROR -> Value.ERROR + 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) { + STARTING -> Known.STARTING + CONNECTED -> Known.CONNECTED + RUNNING -> Known.RUNNING + FINISHED -> Known.FINISHED + ERROR -> Known.ERROR + else -> throw StagehandInvalidDataException("Unknown Status: $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 + + fun validate(): Status = 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 Status && 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 StreamEventSystemDataOutput && + status == other.status && + error == other.error && + result == other.result && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(status, error, result, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "StreamEventSystemDataOutput{status=$status, error=$error, result=$result, additionalProperties=$additionalProperties}" + } + + class StreamEventLogDataOutput + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val message: JsonField, + private val status: JsonValue, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("message") + @ExcludeMissing + message: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonValue = JsonMissing.of(), + ) : this(message, status, mutableMapOf()) + + /** + * Log message from the operation + * + * @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 message(): String = message.getRequired("message") + + /** + * Expected to always return the following: + * ```java + * JsonValue.from("running") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonValue = status + + /** + * Returns the raw JSON value of [message]. + * + * Unlike [message], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("message") @ExcludeMissing fun _message(): JsonField = message + + @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 + * [StreamEventLogDataOutput]. + * + * The following fields are required: + * ```java + * .message() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [StreamEventLogDataOutput]. */ + class Builder internal constructor() { + + private var message: JsonField? = null + private var status: JsonValue = JsonValue.from("running") + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(streamEventLogDataOutput: StreamEventLogDataOutput) = apply { + message = streamEventLogDataOutput.message + status = streamEventLogDataOutput.status + additionalProperties = + streamEventLogDataOutput.additionalProperties.toMutableMap() + } + + /** Log message from the operation */ + fun message(message: String) = message(JsonField.of(message)) + + /** + * Sets [Builder.message] to an arbitrary JSON value. + * + * You should usually call [Builder.message] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun message(message: JsonField) = apply { this.message = message } + + /** + * 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("running") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun status(status: JsonValue) = apply { this.status = status } + + 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 [StreamEventLogDataOutput]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .message() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): StreamEventLogDataOutput = + StreamEventLogDataOutput( + checkRequired("message", message), + status, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): StreamEventLogDataOutput = apply { + if (validated) { + return@apply + } + + message() + _status().let { + if (it != JsonValue.from("running")) { + throw StagehandInvalidDataException("'status' is invalid, received $it") + } + } + 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 (message.asKnown().isPresent) 1 else 0) + + status.let { if (it == JsonValue.from("running")) 1 else 0 } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is StreamEventLogDataOutput && + message == other.message && + status == other.status && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(message, status, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "StreamEventLogDataOutput{message=$message, status=$status, additionalProperties=$additionalProperties}" + } + } + + /** Type of stream event - system events or log messages */ + 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 SYSTEM = of("system") + + @JvmField val LOG = of("log") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + SYSTEM, + LOG, + } + + /** + * 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 { + SYSTEM, + LOG, + /** 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) { + SYSTEM -> Value.SYSTEM + LOG -> Value.LOG + 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) { + SYSTEM -> Known.SYSTEM + LOG -> Known.LOG + 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 + + 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 StreamEvent && + id == other.id && + data == other.data && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(id, data, type, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "StreamEvent{id=$id, data=$data, type=$type, additionalProperties=$additionalProperties}" +} diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/async/SessionServiceAsync.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/async/SessionServiceAsync.kt index 5c78924..5c9171e 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/async/SessionServiceAsync.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/async/SessionServiceAsync.kt @@ -4,22 +4,25 @@ package com.browserbase.api.services.async import com.browserbase.api.core.ClientOptions import com.browserbase.api.core.RequestOptions +import com.browserbase.api.core.http.AsyncStreamResponse import com.browserbase.api.core.http.HttpResponseFor -import com.browserbase.api.models.sessions.Action +import com.browserbase.api.core.http.StreamResponse import com.browserbase.api.models.sessions.SessionActParams import com.browserbase.api.models.sessions.SessionActResponse import com.browserbase.api.models.sessions.SessionEndParams import com.browserbase.api.models.sessions.SessionEndResponse -import com.browserbase.api.models.sessions.SessionExecuteAgentParams -import com.browserbase.api.models.sessions.SessionExecuteAgentResponse +import com.browserbase.api.models.sessions.SessionExecuteParams +import com.browserbase.api.models.sessions.SessionExecuteResponse import com.browserbase.api.models.sessions.SessionExtractParams import com.browserbase.api.models.sessions.SessionExtractResponse import com.browserbase.api.models.sessions.SessionNavigateParams import com.browserbase.api.models.sessions.SessionNavigateResponse import com.browserbase.api.models.sessions.SessionObserveParams +import com.browserbase.api.models.sessions.SessionObserveResponse import com.browserbase.api.models.sessions.SessionStartParams import com.browserbase.api.models.sessions.SessionStartResponse -import java.util.Optional +import com.browserbase.api.models.sessions.StreamEvent +import com.google.errorprone.annotations.MustBeClosed import java.util.concurrent.CompletableFuture import java.util.function.Consumer @@ -38,19 +41,18 @@ interface SessionServiceAsync { fun withOptions(modifier: Consumer): SessionServiceAsync /** - * Performs a browser action based on natural language instruction or a specific action object - * returned by observe(). + * Executes a browser action using natural language instructions or a predefined Action object. */ - fun act(sessionId: String, params: SessionActParams): CompletableFuture = - act(sessionId, params, RequestOptions.none()) + fun act(id: String, params: SessionActParams): CompletableFuture = + act(id, params, RequestOptions.none()) /** @see act */ fun act( - sessionId: String, + id: String, params: SessionActParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture = - act(params.toBuilder().sessionId(sessionId).build(), requestOptions) + act(params.toBuilder().id(id).build(), requestOptions) /** @see act */ fun act(params: SessionActParams): CompletableFuture = @@ -62,23 +64,46 @@ interface SessionServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture - /** Closes the browser and cleans up all resources associated with the session. */ - fun end(sessionId: String): CompletableFuture = - end(sessionId, SessionEndParams.none()) + /** + * Executes a browser action using natural language instructions or a predefined Action object. + */ + fun actStreaming(id: String, params: SessionActParams): AsyncStreamResponse = + actStreaming(id, params, RequestOptions.none()) + + /** @see actStreaming */ + fun actStreaming( + id: String, + params: SessionActParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): AsyncStreamResponse = + actStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see actStreaming */ + fun actStreaming(params: SessionActParams): AsyncStreamResponse = + actStreaming(params, RequestOptions.none()) + + /** @see actStreaming */ + fun actStreaming( + params: SessionActParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): AsyncStreamResponse + + /** Terminates the browser session and releases all associated resources. */ + fun end(id: String): CompletableFuture = end(id, SessionEndParams.none()) /** @see end */ fun end( - sessionId: String, + id: String, params: SessionEndParams = SessionEndParams.none(), requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture = - end(params.toBuilder().sessionId(sessionId).build(), requestOptions) + end(params.toBuilder().id(id).build(), requestOptions) /** @see end */ fun end( - sessionId: String, + id: String, params: SessionEndParams = SessionEndParams.none(), - ): CompletableFuture = end(sessionId, params, RequestOptions.none()) + ): CompletableFuture = end(id, params, RequestOptions.none()) /** @see end */ fun end( @@ -91,58 +116,74 @@ interface SessionServiceAsync { end(params, RequestOptions.none()) /** @see end */ - fun end( - sessionId: String, - requestOptions: RequestOptions, - ): CompletableFuture = - end(sessionId, SessionEndParams.none(), requestOptions) - - /** Runs an autonomous agent that can perform multiple actions to complete a complex task. */ - fun executeAgent( - sessionId: String, - params: SessionExecuteAgentParams, - ): CompletableFuture = - executeAgent(sessionId, params, RequestOptions.none()) - - /** @see executeAgent */ - fun executeAgent( - sessionId: String, - params: SessionExecuteAgentParams, + fun end(id: String, requestOptions: RequestOptions): CompletableFuture = + end(id, SessionEndParams.none(), requestOptions) + + /** Runs an autonomous AI agent that can perform complex multi-step browser tasks. */ + fun execute( + id: String, + params: SessionExecuteParams, + ): CompletableFuture = execute(id, params, RequestOptions.none()) + + /** @see execute */ + fun execute( + id: String, + params: SessionExecuteParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture = - executeAgent(params.toBuilder().sessionId(sessionId).build(), requestOptions) + ): CompletableFuture = + execute(params.toBuilder().id(id).build(), requestOptions) - /** @see executeAgent */ - fun executeAgent( - params: SessionExecuteAgentParams - ): CompletableFuture = executeAgent(params, RequestOptions.none()) + /** @see execute */ + fun execute(params: SessionExecuteParams): CompletableFuture = + execute(params, RequestOptions.none()) - /** @see executeAgent */ - fun executeAgent( - params: SessionExecuteAgentParams, + /** @see execute */ + fun execute( + params: SessionExecuteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** Runs an autonomous AI agent that can perform complex multi-step browser tasks. */ + fun executeStreaming( + id: String, + params: SessionExecuteParams, + ): AsyncStreamResponse = executeStreaming(id, params, RequestOptions.none()) + + /** @see executeStreaming */ + fun executeStreaming( + id: String, + params: SessionExecuteParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture + ): AsyncStreamResponse = + executeStreaming(params.toBuilder().id(id).build(), requestOptions) - /** - * Extracts data from the current page using natural language instructions and optional JSON - * schema for structured output. - */ - fun extract(sessionId: String): CompletableFuture = - extract(sessionId, SessionExtractParams.none()) + /** @see executeStreaming */ + fun executeStreaming(params: SessionExecuteParams): AsyncStreamResponse = + executeStreaming(params, RequestOptions.none()) + + /** @see executeStreaming */ + fun executeStreaming( + params: SessionExecuteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): AsyncStreamResponse + + /** Extracts structured data from the current page using AI-powered analysis. */ + fun extract(id: String): CompletableFuture = + extract(id, SessionExtractParams.none()) /** @see extract */ fun extract( - sessionId: String, + id: String, params: SessionExtractParams = SessionExtractParams.none(), requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture = - extract(params.toBuilder().sessionId(sessionId).build(), requestOptions) + extract(params.toBuilder().id(id).build(), requestOptions) /** @see extract */ fun extract( - sessionId: String, + id: String, params: SessionExtractParams = SessionExtractParams.none(), - ): CompletableFuture = extract(sessionId, params, RequestOptions.none()) + ): CompletableFuture = extract(id, params, RequestOptions.none()) /** @see extract */ fun extract( @@ -156,79 +197,149 @@ interface SessionServiceAsync { /** @see extract */ fun extract( - sessionId: String, + id: String, requestOptions: RequestOptions, ): CompletableFuture = - extract(sessionId, SessionExtractParams.none(), requestOptions) + extract(id, SessionExtractParams.none(), requestOptions) + + /** Extracts structured data from the current page using AI-powered analysis. */ + fun extractStreaming(id: String): AsyncStreamResponse = + extractStreaming(id, SessionExtractParams.none()) + + /** @see extractStreaming */ + fun extractStreaming( + id: String, + params: SessionExtractParams = SessionExtractParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): AsyncStreamResponse = + extractStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see extractStreaming */ + fun extractStreaming( + id: String, + params: SessionExtractParams = SessionExtractParams.none(), + ): AsyncStreamResponse = extractStreaming(id, params, RequestOptions.none()) + + /** @see extractStreaming */ + fun extractStreaming( + params: SessionExtractParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): AsyncStreamResponse - /** Navigates the browser to the specified URL and waits for page load. */ + /** @see extractStreaming */ + fun extractStreaming(params: SessionExtractParams): AsyncStreamResponse = + extractStreaming(params, RequestOptions.none()) + + /** @see extractStreaming */ + fun extractStreaming( + id: String, + requestOptions: RequestOptions, + ): AsyncStreamResponse = + extractStreaming(id, SessionExtractParams.none(), requestOptions) + + /** Navigates the browser to the specified URL. */ fun navigate( - sessionId: String, + id: String, params: SessionNavigateParams, - ): CompletableFuture> = - navigate(sessionId, params, RequestOptions.none()) + ): CompletableFuture = navigate(id, params, RequestOptions.none()) /** @see navigate */ fun navigate( - sessionId: String, + id: String, params: SessionNavigateParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture> = - navigate(params.toBuilder().sessionId(sessionId).build(), requestOptions) + ): CompletableFuture = + navigate(params.toBuilder().id(id).build(), requestOptions) /** @see navigate */ - fun navigate( - params: SessionNavigateParams - ): CompletableFuture> = + fun navigate(params: SessionNavigateParams): CompletableFuture = navigate(params, RequestOptions.none()) /** @see navigate */ fun navigate( params: SessionNavigateParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture> + ): CompletableFuture /** - * Returns a list of candidate actions that can be performed on the page, optionally filtered by - * natural language instruction. + * Identifies and returns available actions on the current page that match the given + * instruction. */ - fun observe(sessionId: String): CompletableFuture> = - observe(sessionId, SessionObserveParams.none()) + fun observe(id: String): CompletableFuture = + observe(id, SessionObserveParams.none()) /** @see observe */ fun observe( - sessionId: String, + id: String, params: SessionObserveParams = SessionObserveParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture> = - observe(params.toBuilder().sessionId(sessionId).build(), requestOptions) + ): CompletableFuture = + observe(params.toBuilder().id(id).build(), requestOptions) /** @see observe */ fun observe( - sessionId: String, + id: String, params: SessionObserveParams = SessionObserveParams.none(), - ): CompletableFuture> = observe(sessionId, params, RequestOptions.none()) + ): CompletableFuture = observe(id, params, RequestOptions.none()) /** @see observe */ fun observe( params: SessionObserveParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture> + ): CompletableFuture /** @see observe */ - fun observe(params: SessionObserveParams): CompletableFuture> = + fun observe(params: SessionObserveParams): CompletableFuture = observe(params, RequestOptions.none()) /** @see observe */ fun observe( - sessionId: String, + id: String, + requestOptions: RequestOptions, + ): CompletableFuture = + observe(id, SessionObserveParams.none(), requestOptions) + + /** + * Identifies and returns available actions on the current page that match the given + * instruction. + */ + fun observeStreaming(id: String): AsyncStreamResponse = + observeStreaming(id, SessionObserveParams.none()) + + /** @see observeStreaming */ + fun observeStreaming( + id: String, + params: SessionObserveParams = SessionObserveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): AsyncStreamResponse = + observeStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see observeStreaming */ + fun observeStreaming( + id: String, + params: SessionObserveParams = SessionObserveParams.none(), + ): AsyncStreamResponse = observeStreaming(id, params, RequestOptions.none()) + + /** @see observeStreaming */ + fun observeStreaming( + params: SessionObserveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): AsyncStreamResponse + + /** @see observeStreaming */ + fun observeStreaming(params: SessionObserveParams): AsyncStreamResponse = + observeStreaming(params, RequestOptions.none()) + + /** @see observeStreaming */ + fun observeStreaming( + id: String, requestOptions: RequestOptions, - ): CompletableFuture> = - observe(sessionId, SessionObserveParams.none(), requestOptions) + ): AsyncStreamResponse = + observeStreaming(id, SessionObserveParams.none(), requestOptions) /** - * Initializes a new Stagehand session with a browser instance. Returns a session ID that must - * be used for all subsequent requests. + * Creates a new browser session with the specified configuration. Returns a session ID used for + * all subsequent operations. */ fun start(params: SessionStartParams): CompletableFuture = start(params, RequestOptions.none()) @@ -254,22 +365,22 @@ interface SessionServiceAsync { ): SessionServiceAsync.WithRawResponse /** - * Returns a raw HTTP response for `post /sessions/{sessionId}/act`, but is otherwise the - * same as [SessionServiceAsync.act]. + * Returns a raw HTTP response for `post /v1/sessions/{id}/act`, but is otherwise the same + * as [SessionServiceAsync.act]. */ fun act( - sessionId: String, + id: String, params: SessionActParams, ): CompletableFuture> = - act(sessionId, params, RequestOptions.none()) + act(id, params, RequestOptions.none()) /** @see act */ fun act( - sessionId: String, + id: String, params: SessionActParams, requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> = - act(params.toBuilder().sessionId(sessionId).build(), requestOptions) + act(params.toBuilder().id(id).build(), requestOptions) /** @see act */ fun act(params: SessionActParams): CompletableFuture> = @@ -282,26 +393,60 @@ interface SessionServiceAsync { ): CompletableFuture> /** - * Returns a raw HTTP response for `post /sessions/{sessionId}/end`, but is otherwise the - * same as [SessionServiceAsync.end]. + * Returns a raw HTTP response for `post /v1/sessions/{id}/act`, but is otherwise the same + * as [SessionServiceAsync.actStreaming]. + */ + @MustBeClosed + fun actStreaming( + id: String, + params: SessionActParams, + ): CompletableFuture>> = + actStreaming(id, params, RequestOptions.none()) + + /** @see actStreaming */ + @MustBeClosed + fun actStreaming( + id: String, + params: SessionActParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> = + actStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see actStreaming */ + @MustBeClosed + fun actStreaming( + params: SessionActParams + ): CompletableFuture>> = + actStreaming(params, RequestOptions.none()) + + /** @see actStreaming */ + @MustBeClosed + fun actStreaming( + params: SessionActParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> + + /** + * Returns a raw HTTP response for `post /v1/sessions/{id}/end`, but is otherwise the same + * as [SessionServiceAsync.end]. */ - fun end(sessionId: String): CompletableFuture> = - end(sessionId, SessionEndParams.none()) + fun end(id: String): CompletableFuture> = + end(id, SessionEndParams.none()) /** @see end */ fun end( - sessionId: String, + id: String, params: SessionEndParams = SessionEndParams.none(), requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> = - end(params.toBuilder().sessionId(sessionId).build(), requestOptions) + end(params.toBuilder().id(id).build(), requestOptions) /** @see end */ fun end( - sessionId: String, + id: String, params: SessionEndParams = SessionEndParams.none(), ): CompletableFuture> = - end(sessionId, params, RequestOptions.none()) + end(id, params, RequestOptions.none()) /** @see end */ fun end( @@ -315,62 +460,96 @@ interface SessionServiceAsync { /** @see end */ fun end( - sessionId: String, + id: String, requestOptions: RequestOptions, ): CompletableFuture> = - end(sessionId, SessionEndParams.none(), requestOptions) + end(id, SessionEndParams.none(), requestOptions) /** - * Returns a raw HTTP response for `post /sessions/{sessionId}/agentExecute`, but is - * otherwise the same as [SessionServiceAsync.executeAgent]. + * Returns a raw HTTP response for `post /v1/sessions/{id}/agentExecute`, but is otherwise + * the same as [SessionServiceAsync.execute]. */ - fun executeAgent( - sessionId: String, - params: SessionExecuteAgentParams, - ): CompletableFuture> = - executeAgent(sessionId, params, RequestOptions.none()) - - /** @see executeAgent */ - fun executeAgent( - sessionId: String, - params: SessionExecuteAgentParams, + fun execute( + id: String, + params: SessionExecuteParams, + ): CompletableFuture> = + execute(id, params, RequestOptions.none()) + + /** @see execute */ + fun execute( + id: String, + params: SessionExecuteParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture> = - executeAgent(params.toBuilder().sessionId(sessionId).build(), requestOptions) - - /** @see executeAgent */ - fun executeAgent( - params: SessionExecuteAgentParams - ): CompletableFuture> = - executeAgent(params, RequestOptions.none()) - - /** @see executeAgent */ - fun executeAgent( - params: SessionExecuteAgentParams, + ): CompletableFuture> = + execute(params.toBuilder().id(id).build(), requestOptions) + + /** @see execute */ + fun execute( + params: SessionExecuteParams + ): CompletableFuture> = + execute(params, RequestOptions.none()) + + /** @see execute */ + fun execute( + params: SessionExecuteParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture> + ): CompletableFuture> /** - * Returns a raw HTTP response for `post /sessions/{sessionId}/extract`, but is otherwise - * the same as [SessionServiceAsync.extract]. + * Returns a raw HTTP response for `post /v1/sessions/{id}/agentExecute`, but is otherwise + * the same as [SessionServiceAsync.executeStreaming]. */ - fun extract(sessionId: String): CompletableFuture> = - extract(sessionId, SessionExtractParams.none()) + @MustBeClosed + fun executeStreaming( + id: String, + params: SessionExecuteParams, + ): CompletableFuture>> = + executeStreaming(id, params, RequestOptions.none()) + + /** @see executeStreaming */ + @MustBeClosed + fun executeStreaming( + id: String, + params: SessionExecuteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> = + executeStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see executeStreaming */ + @MustBeClosed + fun executeStreaming( + params: SessionExecuteParams + ): CompletableFuture>> = + executeStreaming(params, RequestOptions.none()) + + /** @see executeStreaming */ + @MustBeClosed + fun executeStreaming( + params: SessionExecuteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> + + /** + * Returns a raw HTTP response for `post /v1/sessions/{id}/extract`, but is otherwise the + * same as [SessionServiceAsync.extract]. + */ + fun extract(id: String): CompletableFuture> = + extract(id, SessionExtractParams.none()) /** @see extract */ fun extract( - sessionId: String, + id: String, params: SessionExtractParams = SessionExtractParams.none(), requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> = - extract(params.toBuilder().sessionId(sessionId).build(), requestOptions) + extract(params.toBuilder().id(id).build(), requestOptions) /** @see extract */ fun extract( - sessionId: String, + id: String, params: SessionExtractParams = SessionExtractParams.none(), ): CompletableFuture> = - extract(sessionId, params, RequestOptions.none()) + extract(id, params, RequestOptions.none()) /** @see extract */ fun extract( @@ -386,83 +565,182 @@ interface SessionServiceAsync { /** @see extract */ fun extract( - sessionId: String, + id: String, requestOptions: RequestOptions, ): CompletableFuture> = - extract(sessionId, SessionExtractParams.none(), requestOptions) + extract(id, SessionExtractParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /v1/sessions/{id}/extract`, but is otherwise the + * same as [SessionServiceAsync.extractStreaming]. + */ + @MustBeClosed + fun extractStreaming( + id: String + ): CompletableFuture>> = + extractStreaming(id, SessionExtractParams.none()) + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + id: String, + params: SessionExtractParams = SessionExtractParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> = + extractStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + id: String, + params: SessionExtractParams = SessionExtractParams.none(), + ): CompletableFuture>> = + extractStreaming(id, params, RequestOptions.none()) + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + params: SessionExtractParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + params: SessionExtractParams + ): CompletableFuture>> = + extractStreaming(params, RequestOptions.none()) + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + id: String, + requestOptions: RequestOptions, + ): CompletableFuture>> = + extractStreaming(id, SessionExtractParams.none(), requestOptions) /** - * Returns a raw HTTP response for `post /sessions/{sessionId}/navigate`, but is otherwise - * the same as [SessionServiceAsync.navigate]. + * Returns a raw HTTP response for `post /v1/sessions/{id}/navigate`, but is otherwise the + * same as [SessionServiceAsync.navigate]. */ fun navigate( - sessionId: String, + id: String, params: SessionNavigateParams, - ): CompletableFuture>> = - navigate(sessionId, params, RequestOptions.none()) + ): CompletableFuture> = + navigate(id, params, RequestOptions.none()) /** @see navigate */ fun navigate( - sessionId: String, + id: String, params: SessionNavigateParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture>> = - navigate(params.toBuilder().sessionId(sessionId).build(), requestOptions) + ): CompletableFuture> = + navigate(params.toBuilder().id(id).build(), requestOptions) /** @see navigate */ fun navigate( params: SessionNavigateParams - ): CompletableFuture>> = + ): CompletableFuture> = navigate(params, RequestOptions.none()) /** @see navigate */ fun navigate( params: SessionNavigateParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture>> + ): CompletableFuture> /** - * Returns a raw HTTP response for `post /sessions/{sessionId}/observe`, but is otherwise - * the same as [SessionServiceAsync.observe]. + * Returns a raw HTTP response for `post /v1/sessions/{id}/observe`, but is otherwise the + * same as [SessionServiceAsync.observe]. */ - fun observe(sessionId: String): CompletableFuture>> = - observe(sessionId, SessionObserveParams.none()) + fun observe(id: String): CompletableFuture> = + observe(id, SessionObserveParams.none()) /** @see observe */ fun observe( - sessionId: String, + id: String, params: SessionObserveParams = SessionObserveParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture>> = - observe(params.toBuilder().sessionId(sessionId).build(), requestOptions) + ): CompletableFuture> = + observe(params.toBuilder().id(id).build(), requestOptions) /** @see observe */ fun observe( - sessionId: String, + id: String, params: SessionObserveParams = SessionObserveParams.none(), - ): CompletableFuture>> = - observe(sessionId, params, RequestOptions.none()) + ): CompletableFuture> = + observe(id, params, RequestOptions.none()) /** @see observe */ fun observe( params: SessionObserveParams, requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture>> + ): CompletableFuture> /** @see observe */ fun observe( params: SessionObserveParams - ): CompletableFuture>> = observe(params, RequestOptions.none()) + ): CompletableFuture> = + observe(params, RequestOptions.none()) /** @see observe */ fun observe( - sessionId: String, + id: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + observe(id, SessionObserveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /v1/sessions/{id}/observe`, but is otherwise the + * same as [SessionServiceAsync.observeStreaming]. + */ + @MustBeClosed + fun observeStreaming( + id: String + ): CompletableFuture>> = + observeStreaming(id, SessionObserveParams.none()) + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming( + id: String, + params: SessionObserveParams = SessionObserveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> = + observeStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming( + id: String, + params: SessionObserveParams = SessionObserveParams.none(), + ): CompletableFuture>> = + observeStreaming(id, params, RequestOptions.none()) + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming( + params: SessionObserveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming( + params: SessionObserveParams + ): CompletableFuture>> = + observeStreaming(params, RequestOptions.none()) + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming( + id: String, requestOptions: RequestOptions, - ): CompletableFuture>> = - observe(sessionId, SessionObserveParams.none(), requestOptions) + ): CompletableFuture>> = + observeStreaming(id, SessionObserveParams.none(), requestOptions) /** - * Returns a raw HTTP response for `post /sessions/start`, but is otherwise the same as + * Returns a raw HTTP response for `post /v1/sessions/start`, but is otherwise the same as * [SessionServiceAsync.start]. */ fun start( diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/async/SessionServiceAsyncImpl.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/async/SessionServiceAsyncImpl.kt index baa7114..82062e4 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/async/SessionServiceAsyncImpl.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/async/SessionServiceAsyncImpl.kt @@ -3,34 +3,41 @@ package com.browserbase.api.services.async import com.browserbase.api.core.ClientOptions +import com.browserbase.api.core.JsonValue import com.browserbase.api.core.RequestOptions import com.browserbase.api.core.checkRequired import com.browserbase.api.core.handlers.errorBodyHandler import com.browserbase.api.core.handlers.errorHandler import com.browserbase.api.core.handlers.jsonHandler +import com.browserbase.api.core.handlers.mapJson +import com.browserbase.api.core.handlers.sseHandler +import com.browserbase.api.core.http.AsyncStreamResponse import com.browserbase.api.core.http.HttpMethod import com.browserbase.api.core.http.HttpRequest import com.browserbase.api.core.http.HttpResponse import com.browserbase.api.core.http.HttpResponse.Handler import com.browserbase.api.core.http.HttpResponseFor +import com.browserbase.api.core.http.StreamResponse import com.browserbase.api.core.http.json +import com.browserbase.api.core.http.map import com.browserbase.api.core.http.parseable +import com.browserbase.api.core.http.toAsync import com.browserbase.api.core.prepareAsync -import com.browserbase.api.models.sessions.Action import com.browserbase.api.models.sessions.SessionActParams import com.browserbase.api.models.sessions.SessionActResponse import com.browserbase.api.models.sessions.SessionEndParams import com.browserbase.api.models.sessions.SessionEndResponse -import com.browserbase.api.models.sessions.SessionExecuteAgentParams -import com.browserbase.api.models.sessions.SessionExecuteAgentResponse +import com.browserbase.api.models.sessions.SessionExecuteParams +import com.browserbase.api.models.sessions.SessionExecuteResponse import com.browserbase.api.models.sessions.SessionExtractParams import com.browserbase.api.models.sessions.SessionExtractResponse import com.browserbase.api.models.sessions.SessionNavigateParams import com.browserbase.api.models.sessions.SessionNavigateResponse import com.browserbase.api.models.sessions.SessionObserveParams +import com.browserbase.api.models.sessions.SessionObserveResponse import com.browserbase.api.models.sessions.SessionStartParams import com.browserbase.api.models.sessions.SessionStartResponse -import java.util.Optional +import com.browserbase.api.models.sessions.StreamEvent import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull @@ -51,49 +58,89 @@ class SessionServiceAsyncImpl internal constructor(private val clientOptions: Cl params: SessionActParams, requestOptions: RequestOptions, ): CompletableFuture = - // post /sessions/{sessionId}/act + // post /v1/sessions/{id}/act withRawResponse().act(params, requestOptions).thenApply { it.parse() } + override fun actStreaming( + params: SessionActParams, + requestOptions: RequestOptions, + ): AsyncStreamResponse = + // post /v1/sessions/{id}/act + withRawResponse() + .actStreaming(params, requestOptions) + .thenApply { it.parse() } + .toAsync(clientOptions.streamHandlerExecutor) + override fun end( params: SessionEndParams, requestOptions: RequestOptions, ): CompletableFuture = - // post /sessions/{sessionId}/end + // post /v1/sessions/{id}/end withRawResponse().end(params, requestOptions).thenApply { it.parse() } - override fun executeAgent( - params: SessionExecuteAgentParams, + override fun execute( + params: SessionExecuteParams, requestOptions: RequestOptions, - ): CompletableFuture = - // post /sessions/{sessionId}/agentExecute - withRawResponse().executeAgent(params, requestOptions).thenApply { it.parse() } + ): CompletableFuture = + // post /v1/sessions/{id}/agentExecute + withRawResponse().execute(params, requestOptions).thenApply { it.parse() } + + override fun executeStreaming( + params: SessionExecuteParams, + requestOptions: RequestOptions, + ): AsyncStreamResponse = + // post /v1/sessions/{id}/agentExecute + withRawResponse() + .executeStreaming(params, requestOptions) + .thenApply { it.parse() } + .toAsync(clientOptions.streamHandlerExecutor) override fun extract( params: SessionExtractParams, requestOptions: RequestOptions, ): CompletableFuture = - // post /sessions/{sessionId}/extract + // post /v1/sessions/{id}/extract withRawResponse().extract(params, requestOptions).thenApply { it.parse() } + override fun extractStreaming( + params: SessionExtractParams, + requestOptions: RequestOptions, + ): AsyncStreamResponse = + // post /v1/sessions/{id}/extract + withRawResponse() + .extractStreaming(params, requestOptions) + .thenApply { it.parse() } + .toAsync(clientOptions.streamHandlerExecutor) + override fun navigate( params: SessionNavigateParams, requestOptions: RequestOptions, - ): CompletableFuture> = - // post /sessions/{sessionId}/navigate + ): CompletableFuture = + // post /v1/sessions/{id}/navigate withRawResponse().navigate(params, requestOptions).thenApply { it.parse() } override fun observe( params: SessionObserveParams, requestOptions: RequestOptions, - ): CompletableFuture> = - // post /sessions/{sessionId}/observe + ): CompletableFuture = + // post /v1/sessions/{id}/observe withRawResponse().observe(params, requestOptions).thenApply { it.parse() } + override fun observeStreaming( + params: SessionObserveParams, + requestOptions: RequestOptions, + ): AsyncStreamResponse = + // post /v1/sessions/{id}/observe + withRawResponse() + .observeStreaming(params, requestOptions) + .thenApply { it.parse() } + .toAsync(clientOptions.streamHandlerExecutor) + override fun start( params: SessionStartParams, requestOptions: RequestOptions, ): CompletableFuture = - // post /sessions/start + // post /v1/sessions/start withRawResponse().start(params, requestOptions).thenApply { it.parse() } class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : @@ -118,12 +165,12 @@ class SessionServiceAsyncImpl internal constructor(private val clientOptions: Cl ): CompletableFuture> { // We check here instead of in the params builder because this can be specified // positionally or in the params class. - checkRequired("sessionId", params.sessionId().getOrNull()) + checkRequired("id", params.id().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) .baseUrl(clientOptions.baseUrl()) - .addPathSegments("sessions", params._pathParam(0), "act") + .addPathSegments("v1", "sessions", params._pathParam(0), "act") .body(json(clientOptions.jsonMapper, params._body())) .build() .prepareAsync(clientOptions, params) @@ -143,6 +190,51 @@ class SessionServiceAsyncImpl internal constructor(private val clientOptions: Cl } } + private val actStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper).mapJson() + + override fun actStreaming( + params: SessionActParams, + requestOptions: RequestOptions, + ): CompletableFuture>> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("id", params.id().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "sessions", params._pathParam(0), "act") + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("streamResponse", JsonValue.from(true)) + .build(), + ) + ) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .let { actStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } + } + } + } + private val endHandler: Handler = jsonHandler(clientOptions.jsonMapper) @@ -152,12 +244,12 @@ class SessionServiceAsyncImpl internal constructor(private val clientOptions: Cl ): CompletableFuture> { // We check here instead of in the params builder because this can be specified // positionally or in the params class. - checkRequired("sessionId", params.sessionId().getOrNull()) + checkRequired("id", params.id().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) .baseUrl(clientOptions.baseUrl()) - .addPathSegments("sessions", params._pathParam(0), "end") + .addPathSegments("v1", "sessions", params._pathParam(0), "end") .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } .build() .prepareAsync(clientOptions, params) @@ -177,21 +269,21 @@ class SessionServiceAsyncImpl internal constructor(private val clientOptions: Cl } } - private val executeAgentHandler: Handler = - jsonHandler(clientOptions.jsonMapper) + private val executeHandler: Handler = + jsonHandler(clientOptions.jsonMapper) - override fun executeAgent( - params: SessionExecuteAgentParams, + override fun execute( + params: SessionExecuteParams, requestOptions: RequestOptions, - ): CompletableFuture> { + ): CompletableFuture> { // We check here instead of in the params builder because this can be specified // positionally or in the params class. - checkRequired("sessionId", params.sessionId().getOrNull()) + checkRequired("id", params.id().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) .baseUrl(clientOptions.baseUrl()) - .addPathSegments("sessions", params._pathParam(0), "agentExecute") + .addPathSegments("v1", "sessions", params._pathParam(0), "agentExecute") .body(json(clientOptions.jsonMapper, params._body())) .build() .prepareAsync(clientOptions, params) @@ -201,7 +293,7 @@ class SessionServiceAsyncImpl internal constructor(private val clientOptions: Cl .thenApply { response -> errorHandler.handle(response).parseable { response - .use { executeAgentHandler.handle(it) } + .use { executeHandler.handle(it) } .also { if (requestOptions.responseValidation!!) { it.validate() @@ -211,6 +303,51 @@ class SessionServiceAsyncImpl internal constructor(private val clientOptions: Cl } } + private val executeStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper).mapJson() + + override fun executeStreaming( + params: SessionExecuteParams, + requestOptions: RequestOptions, + ): CompletableFuture>> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("id", params.id().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "sessions", params._pathParam(0), "agentExecute") + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("streamResponse", JsonValue.from(true)) + .build(), + ) + ) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .let { executeStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } + } + } + } + private val extractHandler: Handler = jsonHandler(clientOptions.jsonMapper) @@ -220,12 +357,12 @@ class SessionServiceAsyncImpl internal constructor(private val clientOptions: Cl ): CompletableFuture> { // We check here instead of in the params builder because this can be specified // positionally or in the params class. - checkRequired("sessionId", params.sessionId().getOrNull()) + checkRequired("id", params.id().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) .baseUrl(clientOptions.baseUrl()) - .addPathSegments("sessions", params._pathParam(0), "extract") + .addPathSegments("v1", "sessions", params._pathParam(0), "extract") .body(json(clientOptions.jsonMapper, params._body())) .build() .prepareAsync(clientOptions, params) @@ -245,21 +382,66 @@ class SessionServiceAsyncImpl internal constructor(private val clientOptions: Cl } } - private val navigateHandler: Handler> = - jsonHandler>(clientOptions.jsonMapper) + private val extractStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper).mapJson() + + override fun extractStreaming( + params: SessionExtractParams, + requestOptions: RequestOptions, + ): CompletableFuture>> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("id", params.id().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "sessions", params._pathParam(0), "extract") + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("streamResponse", JsonValue.from(true)) + .build(), + ) + ) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .let { extractStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } + } + } + } + + private val navigateHandler: Handler = + jsonHandler(clientOptions.jsonMapper) override fun navigate( params: SessionNavigateParams, requestOptions: RequestOptions, - ): CompletableFuture>> { + ): CompletableFuture> { // We check here instead of in the params builder because this can be specified // positionally or in the params class. - checkRequired("sessionId", params.sessionId().getOrNull()) + checkRequired("id", params.id().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) .baseUrl(clientOptions.baseUrl()) - .addPathSegments("sessions", params._pathParam(0), "navigate") + .addPathSegments("v1", "sessions", params._pathParam(0), "navigate") .body(json(clientOptions.jsonMapper, params._body())) .build() .prepareAsync(clientOptions, params) @@ -272,28 +454,28 @@ class SessionServiceAsyncImpl internal constructor(private val clientOptions: Cl .use { navigateHandler.handle(it) } .also { if (requestOptions.responseValidation!!) { - it.ifPresent { it.validate() } + it.validate() } } } } } - private val observeHandler: Handler> = - jsonHandler>(clientOptions.jsonMapper) + private val observeHandler: Handler = + jsonHandler(clientOptions.jsonMapper) override fun observe( params: SessionObserveParams, requestOptions: RequestOptions, - ): CompletableFuture>> { + ): CompletableFuture> { // We check here instead of in the params builder because this can be specified // positionally or in the params class. - checkRequired("sessionId", params.sessionId().getOrNull()) + checkRequired("id", params.id().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) .baseUrl(clientOptions.baseUrl()) - .addPathSegments("sessions", params._pathParam(0), "observe") + .addPathSegments("v1", "sessions", params._pathParam(0), "observe") .body(json(clientOptions.jsonMapper, params._body())) .build() .prepareAsync(clientOptions, params) @@ -306,7 +488,52 @@ class SessionServiceAsyncImpl internal constructor(private val clientOptions: Cl .use { observeHandler.handle(it) } .also { if (requestOptions.responseValidation!!) { - it.forEach { it.validate() } + it.validate() + } + } + } + } + } + + private val observeStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper).mapJson() + + override fun observeStreaming( + params: SessionObserveParams, + requestOptions: RequestOptions, + ): CompletableFuture>> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("id", params.id().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "sessions", params._pathParam(0), "observe") + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("streamResponse", JsonValue.from(true)) + .build(), + ) + ) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .let { observeStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse } } } @@ -324,7 +551,7 @@ class SessionServiceAsyncImpl internal constructor(private val clientOptions: Cl HttpRequest.builder() .method(HttpMethod.POST) .baseUrl(clientOptions.baseUrl()) - .addPathSegments("sessions", "start") + .addPathSegments("v1", "sessions", "start") .body(json(clientOptions.jsonMapper, params._body())) .build() .prepareAsync(clientOptions, params) diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionService.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionService.kt index aac24dc..0d56e93 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionService.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionService.kt @@ -5,22 +5,23 @@ package com.browserbase.api.services.blocking import com.browserbase.api.core.ClientOptions import com.browserbase.api.core.RequestOptions import com.browserbase.api.core.http.HttpResponseFor -import com.browserbase.api.models.sessions.Action +import com.browserbase.api.core.http.StreamResponse import com.browserbase.api.models.sessions.SessionActParams import com.browserbase.api.models.sessions.SessionActResponse import com.browserbase.api.models.sessions.SessionEndParams import com.browserbase.api.models.sessions.SessionEndResponse -import com.browserbase.api.models.sessions.SessionExecuteAgentParams -import com.browserbase.api.models.sessions.SessionExecuteAgentResponse +import com.browserbase.api.models.sessions.SessionExecuteParams +import com.browserbase.api.models.sessions.SessionExecuteResponse import com.browserbase.api.models.sessions.SessionExtractParams import com.browserbase.api.models.sessions.SessionExtractResponse import com.browserbase.api.models.sessions.SessionNavigateParams import com.browserbase.api.models.sessions.SessionNavigateResponse import com.browserbase.api.models.sessions.SessionObserveParams +import com.browserbase.api.models.sessions.SessionObserveResponse import com.browserbase.api.models.sessions.SessionStartParams import com.browserbase.api.models.sessions.SessionStartResponse +import com.browserbase.api.models.sessions.StreamEvent import com.google.errorprone.annotations.MustBeClosed -import java.util.Optional import java.util.function.Consumer interface SessionService { @@ -38,18 +39,17 @@ interface SessionService { fun withOptions(modifier: Consumer): SessionService /** - * Performs a browser action based on natural language instruction or a specific action object - * returned by observe(). + * Executes a browser action using natural language instructions or a predefined Action object. */ - fun act(sessionId: String, params: SessionActParams): SessionActResponse = - act(sessionId, params, RequestOptions.none()) + fun act(id: String, params: SessionActParams): SessionActResponse = + act(id, params, RequestOptions.none()) /** @see act */ fun act( - sessionId: String, + id: String, params: SessionActParams, requestOptions: RequestOptions = RequestOptions.none(), - ): SessionActResponse = act(params.toBuilder().sessionId(sessionId).build(), requestOptions) + ): SessionActResponse = act(params.toBuilder().id(id).build(), requestOptions) /** @see act */ fun act(params: SessionActParams): SessionActResponse = act(params, RequestOptions.none()) @@ -60,21 +60,46 @@ interface SessionService { requestOptions: RequestOptions = RequestOptions.none(), ): SessionActResponse - /** Closes the browser and cleans up all resources associated with the session. */ - fun end(sessionId: String): SessionEndResponse = end(sessionId, SessionEndParams.none()) + /** + * Executes a browser action using natural language instructions or a predefined Action object. + */ + @MustBeClosed + fun actStreaming(id: String, params: SessionActParams): StreamResponse = + actStreaming(id, params, RequestOptions.none()) + + /** @see actStreaming */ + @MustBeClosed + fun actStreaming( + id: String, + params: SessionActParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): StreamResponse = actStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see actStreaming */ + @MustBeClosed + fun actStreaming(params: SessionActParams): StreamResponse = + actStreaming(params, RequestOptions.none()) + + /** @see actStreaming */ + @MustBeClosed + fun actStreaming( + params: SessionActParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): StreamResponse + + /** Terminates the browser session and releases all associated resources. */ + fun end(id: String): SessionEndResponse = end(id, SessionEndParams.none()) /** @see end */ fun end( - sessionId: String, + id: String, params: SessionEndParams = SessionEndParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): SessionEndResponse = end(params.toBuilder().sessionId(sessionId).build(), requestOptions) + ): SessionEndResponse = end(params.toBuilder().id(id).build(), requestOptions) /** @see end */ - fun end( - sessionId: String, - params: SessionEndParams = SessionEndParams.none(), - ): SessionEndResponse = end(sessionId, params, RequestOptions.none()) + fun end(id: String, params: SessionEndParams = SessionEndParams.none()): SessionEndResponse = + end(id, params, RequestOptions.none()) /** @see end */ fun end( @@ -86,53 +111,71 @@ interface SessionService { fun end(params: SessionEndParams): SessionEndResponse = end(params, RequestOptions.none()) /** @see end */ - fun end(sessionId: String, requestOptions: RequestOptions): SessionEndResponse = - end(sessionId, SessionEndParams.none(), requestOptions) - - /** Runs an autonomous agent that can perform multiple actions to complete a complex task. */ - fun executeAgent( - sessionId: String, - params: SessionExecuteAgentParams, - ): SessionExecuteAgentResponse = executeAgent(sessionId, params, RequestOptions.none()) - - /** @see executeAgent */ - fun executeAgent( - sessionId: String, - params: SessionExecuteAgentParams, + fun end(id: String, requestOptions: RequestOptions): SessionEndResponse = + end(id, SessionEndParams.none(), requestOptions) + + /** Runs an autonomous AI agent that can perform complex multi-step browser tasks. */ + fun execute(id: String, params: SessionExecuteParams): SessionExecuteResponse = + execute(id, params, RequestOptions.none()) + + /** @see execute */ + fun execute( + id: String, + params: SessionExecuteParams, requestOptions: RequestOptions = RequestOptions.none(), - ): SessionExecuteAgentResponse = - executeAgent(params.toBuilder().sessionId(sessionId).build(), requestOptions) + ): SessionExecuteResponse = execute(params.toBuilder().id(id).build(), requestOptions) - /** @see executeAgent */ - fun executeAgent(params: SessionExecuteAgentParams): SessionExecuteAgentResponse = - executeAgent(params, RequestOptions.none()) + /** @see execute */ + fun execute(params: SessionExecuteParams): SessionExecuteResponse = + execute(params, RequestOptions.none()) - /** @see executeAgent */ - fun executeAgent( - params: SessionExecuteAgentParams, + /** @see execute */ + fun execute( + params: SessionExecuteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): SessionExecuteResponse + + /** Runs an autonomous AI agent that can perform complex multi-step browser tasks. */ + @MustBeClosed + fun executeStreaming(id: String, params: SessionExecuteParams): StreamResponse = + executeStreaming(id, params, RequestOptions.none()) + + /** @see executeStreaming */ + @MustBeClosed + fun executeStreaming( + id: String, + params: SessionExecuteParams, requestOptions: RequestOptions = RequestOptions.none(), - ): SessionExecuteAgentResponse + ): StreamResponse = + executeStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see executeStreaming */ + @MustBeClosed + fun executeStreaming(params: SessionExecuteParams): StreamResponse = + executeStreaming(params, RequestOptions.none()) + + /** @see executeStreaming */ + @MustBeClosed + fun executeStreaming( + params: SessionExecuteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): StreamResponse - /** - * Extracts data from the current page using natural language instructions and optional JSON - * schema for structured output. - */ - fun extract(sessionId: String): SessionExtractResponse = - extract(sessionId, SessionExtractParams.none()) + /** Extracts structured data from the current page using AI-powered analysis. */ + fun extract(id: String): SessionExtractResponse = extract(id, SessionExtractParams.none()) /** @see extract */ fun extract( - sessionId: String, + id: String, params: SessionExtractParams = SessionExtractParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): SessionExtractResponse = - extract(params.toBuilder().sessionId(sessionId).build(), requestOptions) + ): SessionExtractResponse = extract(params.toBuilder().id(id).build(), requestOptions) /** @see extract */ fun extract( - sessionId: String, + id: String, params: SessionExtractParams = SessionExtractParams.none(), - ): SessionExtractResponse = extract(sessionId, params, RequestOptions.none()) + ): SessionExtractResponse = extract(id, params, RequestOptions.none()) /** @see extract */ fun extract( @@ -145,68 +188,145 @@ interface SessionService { extract(params, RequestOptions.none()) /** @see extract */ - fun extract(sessionId: String, requestOptions: RequestOptions): SessionExtractResponse = - extract(sessionId, SessionExtractParams.none(), requestOptions) + fun extract(id: String, requestOptions: RequestOptions): SessionExtractResponse = + extract(id, SessionExtractParams.none(), requestOptions) + + /** Extracts structured data from the current page using AI-powered analysis. */ + @MustBeClosed + fun extractStreaming(id: String): StreamResponse = + extractStreaming(id, SessionExtractParams.none()) + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + id: String, + params: SessionExtractParams = SessionExtractParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): StreamResponse = + extractStreaming(params.toBuilder().id(id).build(), requestOptions) - /** Navigates the browser to the specified URL and waits for page load. */ - fun navigate( - sessionId: String, - params: SessionNavigateParams, - ): Optional = navigate(sessionId, params, RequestOptions.none()) + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + id: String, + params: SessionExtractParams = SessionExtractParams.none(), + ): StreamResponse = extractStreaming(id, params, RequestOptions.none()) + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + params: SessionExtractParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): StreamResponse + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming(params: SessionExtractParams): StreamResponse = + extractStreaming(params, RequestOptions.none()) + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming(id: String, requestOptions: RequestOptions): StreamResponse = + extractStreaming(id, SessionExtractParams.none(), requestOptions) + + /** Navigates the browser to the specified URL. */ + fun navigate(id: String, params: SessionNavigateParams): SessionNavigateResponse = + navigate(id, params, RequestOptions.none()) /** @see navigate */ fun navigate( - sessionId: String, + id: String, params: SessionNavigateParams, requestOptions: RequestOptions = RequestOptions.none(), - ): Optional = - navigate(params.toBuilder().sessionId(sessionId).build(), requestOptions) + ): SessionNavigateResponse = navigate(params.toBuilder().id(id).build(), requestOptions) /** @see navigate */ - fun navigate(params: SessionNavigateParams): Optional = + fun navigate(params: SessionNavigateParams): SessionNavigateResponse = navigate(params, RequestOptions.none()) /** @see navigate */ fun navigate( params: SessionNavigateParams, requestOptions: RequestOptions = RequestOptions.none(), - ): Optional + ): SessionNavigateResponse /** - * Returns a list of candidate actions that can be performed on the page, optionally filtered by - * natural language instruction. + * Identifies and returns available actions on the current page that match the given + * instruction. */ - fun observe(sessionId: String): List = observe(sessionId, SessionObserveParams.none()) + fun observe(id: String): SessionObserveResponse = observe(id, SessionObserveParams.none()) /** @see observe */ fun observe( - sessionId: String, + id: String, params: SessionObserveParams = SessionObserveParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): List = observe(params.toBuilder().sessionId(sessionId).build(), requestOptions) + ): SessionObserveResponse = observe(params.toBuilder().id(id).build(), requestOptions) /** @see observe */ fun observe( - sessionId: String, + id: String, params: SessionObserveParams = SessionObserveParams.none(), - ): List = observe(sessionId, params, RequestOptions.none()) + ): SessionObserveResponse = observe(id, params, RequestOptions.none()) /** @see observe */ fun observe( params: SessionObserveParams, requestOptions: RequestOptions = RequestOptions.none(), - ): List + ): SessionObserveResponse /** @see observe */ - fun observe(params: SessionObserveParams): List = observe(params, RequestOptions.none()) + fun observe(params: SessionObserveParams): SessionObserveResponse = + observe(params, RequestOptions.none()) /** @see observe */ - fun observe(sessionId: String, requestOptions: RequestOptions): List = - observe(sessionId, SessionObserveParams.none(), requestOptions) + fun observe(id: String, requestOptions: RequestOptions): SessionObserveResponse = + observe(id, SessionObserveParams.none(), requestOptions) /** - * Initializes a new Stagehand session with a browser instance. Returns a session ID that must - * be used for all subsequent requests. + * Identifies and returns available actions on the current page that match the given + * instruction. + */ + @MustBeClosed + fun observeStreaming(id: String): StreamResponse = + observeStreaming(id, SessionObserveParams.none()) + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming( + id: String, + params: SessionObserveParams = SessionObserveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): StreamResponse = + observeStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming( + id: String, + params: SessionObserveParams = SessionObserveParams.none(), + ): StreamResponse = observeStreaming(id, params, RequestOptions.none()) + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming( + params: SessionObserveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): StreamResponse + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming(params: SessionObserveParams): StreamResponse = + observeStreaming(params, RequestOptions.none()) + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming(id: String, requestOptions: RequestOptions): StreamResponse = + observeStreaming(id, SessionObserveParams.none(), requestOptions) + + /** + * Creates a new browser session with the specified configuration. Returns a session ID used for + * all subsequent operations. */ fun start(params: SessionStartParams): SessionStartResponse = start(params, RequestOptions.none()) @@ -228,21 +348,21 @@ interface SessionService { fun withOptions(modifier: Consumer): SessionService.WithRawResponse /** - * Returns a raw HTTP response for `post /sessions/{sessionId}/act`, but is otherwise the - * same as [SessionService.act]. + * Returns a raw HTTP response for `post /v1/sessions/{id}/act`, but is otherwise the same + * as [SessionService.act]. */ @MustBeClosed - fun act(sessionId: String, params: SessionActParams): HttpResponseFor = - act(sessionId, params, RequestOptions.none()) + fun act(id: String, params: SessionActParams): HttpResponseFor = + act(id, params, RequestOptions.none()) /** @see act */ @MustBeClosed fun act( - sessionId: String, + id: String, params: SessionActParams, requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor = - act(params.toBuilder().sessionId(sessionId).build(), requestOptions) + act(params.toBuilder().id(id).build(), requestOptions) /** @see act */ @MustBeClosed @@ -257,28 +377,59 @@ interface SessionService { ): HttpResponseFor /** - * Returns a raw HTTP response for `post /sessions/{sessionId}/end`, but is otherwise the - * same as [SessionService.end]. + * Returns a raw HTTP response for `post /v1/sessions/{id}/act`, but is otherwise the same + * as [SessionService.actStreaming]. + */ + @MustBeClosed + fun actStreaming( + id: String, + params: SessionActParams, + ): HttpResponseFor> = + actStreaming(id, params, RequestOptions.none()) + + /** @see actStreaming */ + @MustBeClosed + fun actStreaming( + id: String, + params: SessionActParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> = + actStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see actStreaming */ + @MustBeClosed + fun actStreaming(params: SessionActParams): HttpResponseFor> = + actStreaming(params, RequestOptions.none()) + + /** @see actStreaming */ + @MustBeClosed + fun actStreaming( + params: SessionActParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> + + /** + * Returns a raw HTTP response for `post /v1/sessions/{id}/end`, but is otherwise the same + * as [SessionService.end]. */ @MustBeClosed - fun end(sessionId: String): HttpResponseFor = - end(sessionId, SessionEndParams.none()) + fun end(id: String): HttpResponseFor = end(id, SessionEndParams.none()) /** @see end */ @MustBeClosed fun end( - sessionId: String, + id: String, params: SessionEndParams = SessionEndParams.none(), requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor = - end(params.toBuilder().sessionId(sessionId).build(), requestOptions) + end(params.toBuilder().id(id).build(), requestOptions) /** @see end */ @MustBeClosed fun end( - sessionId: String, + id: String, params: SessionEndParams = SessionEndParams.none(), - ): HttpResponseFor = end(sessionId, params, RequestOptions.none()) + ): HttpResponseFor = end(id, params, RequestOptions.none()) /** @see end */ @MustBeClosed @@ -294,70 +445,97 @@ interface SessionService { /** @see end */ @MustBeClosed - fun end( - sessionId: String, - requestOptions: RequestOptions, - ): HttpResponseFor = - end(sessionId, SessionEndParams.none(), requestOptions) + fun end(id: String, requestOptions: RequestOptions): HttpResponseFor = + end(id, SessionEndParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /v1/sessions/{id}/agentExecute`, but is otherwise + * the same as [SessionService.execute]. + */ + @MustBeClosed + fun execute( + id: String, + params: SessionExecuteParams, + ): HttpResponseFor = execute(id, params, RequestOptions.none()) + + /** @see execute */ + @MustBeClosed + fun execute( + id: String, + params: SessionExecuteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + execute(params.toBuilder().id(id).build(), requestOptions) + + /** @see execute */ + @MustBeClosed + fun execute(params: SessionExecuteParams): HttpResponseFor = + execute(params, RequestOptions.none()) + + /** @see execute */ + @MustBeClosed + fun execute( + params: SessionExecuteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor /** - * Returns a raw HTTP response for `post /sessions/{sessionId}/agentExecute`, but is - * otherwise the same as [SessionService.executeAgent]. + * Returns a raw HTTP response for `post /v1/sessions/{id}/agentExecute`, but is otherwise + * the same as [SessionService.executeStreaming]. */ @MustBeClosed - fun executeAgent( - sessionId: String, - params: SessionExecuteAgentParams, - ): HttpResponseFor = - executeAgent(sessionId, params, RequestOptions.none()) + fun executeStreaming( + id: String, + params: SessionExecuteParams, + ): HttpResponseFor> = + executeStreaming(id, params, RequestOptions.none()) - /** @see executeAgent */ + /** @see executeStreaming */ @MustBeClosed - fun executeAgent( - sessionId: String, - params: SessionExecuteAgentParams, + fun executeStreaming( + id: String, + params: SessionExecuteParams, requestOptions: RequestOptions = RequestOptions.none(), - ): HttpResponseFor = - executeAgent(params.toBuilder().sessionId(sessionId).build(), requestOptions) + ): HttpResponseFor> = + executeStreaming(params.toBuilder().id(id).build(), requestOptions) - /** @see executeAgent */ + /** @see executeStreaming */ @MustBeClosed - fun executeAgent( - params: SessionExecuteAgentParams - ): HttpResponseFor = - executeAgent(params, RequestOptions.none()) + fun executeStreaming( + params: SessionExecuteParams + ): HttpResponseFor> = + executeStreaming(params, RequestOptions.none()) - /** @see executeAgent */ + /** @see executeStreaming */ @MustBeClosed - fun executeAgent( - params: SessionExecuteAgentParams, + fun executeStreaming( + params: SessionExecuteParams, requestOptions: RequestOptions = RequestOptions.none(), - ): HttpResponseFor + ): HttpResponseFor> /** - * Returns a raw HTTP response for `post /sessions/{sessionId}/extract`, but is otherwise - * the same as [SessionService.extract]. + * Returns a raw HTTP response for `post /v1/sessions/{id}/extract`, but is otherwise the + * same as [SessionService.extract]. */ @MustBeClosed - fun extract(sessionId: String): HttpResponseFor = - extract(sessionId, SessionExtractParams.none()) + fun extract(id: String): HttpResponseFor = + extract(id, SessionExtractParams.none()) /** @see extract */ @MustBeClosed fun extract( - sessionId: String, + id: String, params: SessionExtractParams = SessionExtractParams.none(), requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor = - extract(params.toBuilder().sessionId(sessionId).build(), requestOptions) + extract(params.toBuilder().id(id).build(), requestOptions) /** @see extract */ @MustBeClosed fun extract( - sessionId: String, + id: String, params: SessionExtractParams = SessionExtractParams.none(), - ): HttpResponseFor = - extract(sessionId, params, RequestOptions.none()) + ): HttpResponseFor = extract(id, params, RequestOptions.none()) /** @see extract */ @MustBeClosed @@ -374,36 +552,80 @@ interface SessionService { /** @see extract */ @MustBeClosed fun extract( - sessionId: String, + id: String, requestOptions: RequestOptions, ): HttpResponseFor = - extract(sessionId, SessionExtractParams.none(), requestOptions) + extract(id, SessionExtractParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /v1/sessions/{id}/extract`, but is otherwise the + * same as [SessionService.extractStreaming]. + */ + @MustBeClosed + fun extractStreaming(id: String): HttpResponseFor> = + extractStreaming(id, SessionExtractParams.none()) + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + id: String, + params: SessionExtractParams = SessionExtractParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> = + extractStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + id: String, + params: SessionExtractParams = SessionExtractParams.none(), + ): HttpResponseFor> = + extractStreaming(id, params, RequestOptions.none()) + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + params: SessionExtractParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + params: SessionExtractParams + ): HttpResponseFor> = + extractStreaming(params, RequestOptions.none()) + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + id: String, + requestOptions: RequestOptions, + ): HttpResponseFor> = + extractStreaming(id, SessionExtractParams.none(), requestOptions) /** - * Returns a raw HTTP response for `post /sessions/{sessionId}/navigate`, but is otherwise - * the same as [SessionService.navigate]. + * Returns a raw HTTP response for `post /v1/sessions/{id}/navigate`, but is otherwise the + * same as [SessionService.navigate]. */ @MustBeClosed fun navigate( - sessionId: String, + id: String, params: SessionNavigateParams, - ): HttpResponseFor> = - navigate(sessionId, params, RequestOptions.none()) + ): HttpResponseFor = navigate(id, params, RequestOptions.none()) /** @see navigate */ @MustBeClosed fun navigate( - sessionId: String, + id: String, params: SessionNavigateParams, requestOptions: RequestOptions = RequestOptions.none(), - ): HttpResponseFor> = - navigate(params.toBuilder().sessionId(sessionId).build(), requestOptions) + ): HttpResponseFor = + navigate(params.toBuilder().id(id).build(), requestOptions) /** @see navigate */ @MustBeClosed - fun navigate( - params: SessionNavigateParams - ): HttpResponseFor> = + fun navigate(params: SessionNavigateParams): HttpResponseFor = navigate(params, RequestOptions.none()) /** @see navigate */ @@ -411,54 +633,101 @@ interface SessionService { fun navigate( params: SessionNavigateParams, requestOptions: RequestOptions = RequestOptions.none(), - ): HttpResponseFor> + ): HttpResponseFor /** - * Returns a raw HTTP response for `post /sessions/{sessionId}/observe`, but is otherwise - * the same as [SessionService.observe]. + * Returns a raw HTTP response for `post /v1/sessions/{id}/observe`, but is otherwise the + * same as [SessionService.observe]. */ @MustBeClosed - fun observe(sessionId: String): HttpResponseFor> = - observe(sessionId, SessionObserveParams.none()) + fun observe(id: String): HttpResponseFor = + observe(id, SessionObserveParams.none()) /** @see observe */ @MustBeClosed fun observe( - sessionId: String, + id: String, params: SessionObserveParams = SessionObserveParams.none(), requestOptions: RequestOptions = RequestOptions.none(), - ): HttpResponseFor> = - observe(params.toBuilder().sessionId(sessionId).build(), requestOptions) + ): HttpResponseFor = + observe(params.toBuilder().id(id).build(), requestOptions) /** @see observe */ @MustBeClosed fun observe( - sessionId: String, + id: String, params: SessionObserveParams = SessionObserveParams.none(), - ): HttpResponseFor> = observe(sessionId, params, RequestOptions.none()) + ): HttpResponseFor = observe(id, params, RequestOptions.none()) /** @see observe */ @MustBeClosed fun observe( params: SessionObserveParams, requestOptions: RequestOptions = RequestOptions.none(), - ): HttpResponseFor> + ): HttpResponseFor /** @see observe */ @MustBeClosed - fun observe(params: SessionObserveParams): HttpResponseFor> = + fun observe(params: SessionObserveParams): HttpResponseFor = observe(params, RequestOptions.none()) /** @see observe */ @MustBeClosed fun observe( - sessionId: String, + id: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + observe(id, SessionObserveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /v1/sessions/{id}/observe`, but is otherwise the + * same as [SessionService.observeStreaming]. + */ + @MustBeClosed + fun observeStreaming(id: String): HttpResponseFor> = + observeStreaming(id, SessionObserveParams.none()) + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming( + id: String, + params: SessionObserveParams = SessionObserveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> = + observeStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming( + id: String, + params: SessionObserveParams = SessionObserveParams.none(), + ): HttpResponseFor> = + observeStreaming(id, params, RequestOptions.none()) + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming( + params: SessionObserveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming( + params: SessionObserveParams + ): HttpResponseFor> = + observeStreaming(params, RequestOptions.none()) + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming( + id: String, requestOptions: RequestOptions, - ): HttpResponseFor> = - observe(sessionId, SessionObserveParams.none(), requestOptions) + ): HttpResponseFor> = + observeStreaming(id, SessionObserveParams.none(), requestOptions) /** - * Returns a raw HTTP response for `post /sessions/start`, but is otherwise the same as + * Returns a raw HTTP response for `post /v1/sessions/start`, but is otherwise the same as * [SessionService.start]. */ @MustBeClosed diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionServiceImpl.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionServiceImpl.kt index 6c09595..4bfce1b 100644 --- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionServiceImpl.kt +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionServiceImpl.kt @@ -3,34 +3,39 @@ package com.browserbase.api.services.blocking import com.browserbase.api.core.ClientOptions +import com.browserbase.api.core.JsonValue import com.browserbase.api.core.RequestOptions import com.browserbase.api.core.checkRequired import com.browserbase.api.core.handlers.errorBodyHandler import com.browserbase.api.core.handlers.errorHandler import com.browserbase.api.core.handlers.jsonHandler +import com.browserbase.api.core.handlers.mapJson +import com.browserbase.api.core.handlers.sseHandler import com.browserbase.api.core.http.HttpMethod import com.browserbase.api.core.http.HttpRequest import com.browserbase.api.core.http.HttpResponse import com.browserbase.api.core.http.HttpResponse.Handler import com.browserbase.api.core.http.HttpResponseFor +import com.browserbase.api.core.http.StreamResponse import com.browserbase.api.core.http.json +import com.browserbase.api.core.http.map import com.browserbase.api.core.http.parseable import com.browserbase.api.core.prepare -import com.browserbase.api.models.sessions.Action import com.browserbase.api.models.sessions.SessionActParams import com.browserbase.api.models.sessions.SessionActResponse import com.browserbase.api.models.sessions.SessionEndParams import com.browserbase.api.models.sessions.SessionEndResponse -import com.browserbase.api.models.sessions.SessionExecuteAgentParams -import com.browserbase.api.models.sessions.SessionExecuteAgentResponse +import com.browserbase.api.models.sessions.SessionExecuteParams +import com.browserbase.api.models.sessions.SessionExecuteResponse import com.browserbase.api.models.sessions.SessionExtractParams import com.browserbase.api.models.sessions.SessionExtractResponse import com.browserbase.api.models.sessions.SessionNavigateParams import com.browserbase.api.models.sessions.SessionNavigateResponse import com.browserbase.api.models.sessions.SessionObserveParams +import com.browserbase.api.models.sessions.SessionObserveResponse import com.browserbase.api.models.sessions.SessionStartParams import com.browserbase.api.models.sessions.SessionStartResponse -import java.util.Optional +import com.browserbase.api.models.sessions.StreamEvent import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull @@ -47,46 +52,74 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO SessionServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) override fun act(params: SessionActParams, requestOptions: RequestOptions): SessionActResponse = - // post /sessions/{sessionId}/act + // post /v1/sessions/{id}/act withRawResponse().act(params, requestOptions).parse() + override fun actStreaming( + params: SessionActParams, + requestOptions: RequestOptions, + ): StreamResponse = + // post /v1/sessions/{id}/act + withRawResponse().actStreaming(params, requestOptions).parse() + override fun end(params: SessionEndParams, requestOptions: RequestOptions): SessionEndResponse = - // post /sessions/{sessionId}/end + // post /v1/sessions/{id}/end withRawResponse().end(params, requestOptions).parse() - override fun executeAgent( - params: SessionExecuteAgentParams, + override fun execute( + params: SessionExecuteParams, + requestOptions: RequestOptions, + ): SessionExecuteResponse = + // post /v1/sessions/{id}/agentExecute + withRawResponse().execute(params, requestOptions).parse() + + override fun executeStreaming( + params: SessionExecuteParams, requestOptions: RequestOptions, - ): SessionExecuteAgentResponse = - // post /sessions/{sessionId}/agentExecute - withRawResponse().executeAgent(params, requestOptions).parse() + ): StreamResponse = + // post /v1/sessions/{id}/agentExecute + withRawResponse().executeStreaming(params, requestOptions).parse() override fun extract( params: SessionExtractParams, requestOptions: RequestOptions, ): SessionExtractResponse = - // post /sessions/{sessionId}/extract + // post /v1/sessions/{id}/extract withRawResponse().extract(params, requestOptions).parse() + override fun extractStreaming( + params: SessionExtractParams, + requestOptions: RequestOptions, + ): StreamResponse = + // post /v1/sessions/{id}/extract + withRawResponse().extractStreaming(params, requestOptions).parse() + override fun navigate( params: SessionNavigateParams, requestOptions: RequestOptions, - ): Optional = - // post /sessions/{sessionId}/navigate + ): SessionNavigateResponse = + // post /v1/sessions/{id}/navigate withRawResponse().navigate(params, requestOptions).parse() override fun observe( params: SessionObserveParams, requestOptions: RequestOptions, - ): List = - // post /sessions/{sessionId}/observe + ): SessionObserveResponse = + // post /v1/sessions/{id}/observe withRawResponse().observe(params, requestOptions).parse() + override fun observeStreaming( + params: SessionObserveParams, + requestOptions: RequestOptions, + ): StreamResponse = + // post /v1/sessions/{id}/observe + withRawResponse().observeStreaming(params, requestOptions).parse() + override fun start( params: SessionStartParams, requestOptions: RequestOptions, ): SessionStartResponse = - // post /sessions/start + // post /v1/sessions/start withRawResponse().start(params, requestOptions).parse() class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : @@ -111,12 +144,12 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO ): HttpResponseFor { // We check here instead of in the params builder because this can be specified // positionally or in the params class. - checkRequired("sessionId", params.sessionId().getOrNull()) + checkRequired("id", params.id().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) .baseUrl(clientOptions.baseUrl()) - .addPathSegments("sessions", params._pathParam(0), "act") + .addPathSegments("v1", "sessions", params._pathParam(0), "act") .body(json(clientOptions.jsonMapper, params._body())) .build() .prepare(clientOptions, params) @@ -133,6 +166,48 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO } } + private val actStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper).mapJson() + + override fun actStreaming( + params: SessionActParams, + requestOptions: RequestOptions, + ): HttpResponseFor> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("id", params.id().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "sessions", params._pathParam(0), "act") + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("streamResponse", JsonValue.from(true)) + .build(), + ) + ) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .let { actStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } + } + } + private val endHandler: Handler = jsonHandler(clientOptions.jsonMapper) @@ -142,12 +217,12 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO ): HttpResponseFor { // We check here instead of in the params builder because this can be specified // positionally or in the params class. - checkRequired("sessionId", params.sessionId().getOrNull()) + checkRequired("id", params.id().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) .baseUrl(clientOptions.baseUrl()) - .addPathSegments("sessions", params._pathParam(0), "end") + .addPathSegments("v1", "sessions", params._pathParam(0), "end") .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } .build() .prepare(clientOptions, params) @@ -164,21 +239,21 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO } } - private val executeAgentHandler: Handler = - jsonHandler(clientOptions.jsonMapper) + private val executeHandler: Handler = + jsonHandler(clientOptions.jsonMapper) - override fun executeAgent( - params: SessionExecuteAgentParams, + override fun execute( + params: SessionExecuteParams, requestOptions: RequestOptions, - ): HttpResponseFor { + ): HttpResponseFor { // We check here instead of in the params builder because this can be specified // positionally or in the params class. - checkRequired("sessionId", params.sessionId().getOrNull()) + checkRequired("id", params.id().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) .baseUrl(clientOptions.baseUrl()) - .addPathSegments("sessions", params._pathParam(0), "agentExecute") + .addPathSegments("v1", "sessions", params._pathParam(0), "agentExecute") .body(json(clientOptions.jsonMapper, params._body())) .build() .prepare(clientOptions, params) @@ -186,7 +261,7 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO val response = clientOptions.httpClient.execute(request, requestOptions) return errorHandler.handle(response).parseable { response - .use { executeAgentHandler.handle(it) } + .use { executeHandler.handle(it) } .also { if (requestOptions.responseValidation!!) { it.validate() @@ -195,6 +270,48 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO } } + private val executeStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper).mapJson() + + override fun executeStreaming( + params: SessionExecuteParams, + requestOptions: RequestOptions, + ): HttpResponseFor> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("id", params.id().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "sessions", params._pathParam(0), "agentExecute") + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("streamResponse", JsonValue.from(true)) + .build(), + ) + ) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .let { executeStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } + } + } + private val extractHandler: Handler = jsonHandler(clientOptions.jsonMapper) @@ -204,12 +321,12 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO ): HttpResponseFor { // We check here instead of in the params builder because this can be specified // positionally or in the params class. - checkRequired("sessionId", params.sessionId().getOrNull()) + checkRequired("id", params.id().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) .baseUrl(clientOptions.baseUrl()) - .addPathSegments("sessions", params._pathParam(0), "extract") + .addPathSegments("v1", "sessions", params._pathParam(0), "extract") .body(json(clientOptions.jsonMapper, params._body())) .build() .prepare(clientOptions, params) @@ -226,21 +343,63 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO } } - private val navigateHandler: Handler> = - jsonHandler>(clientOptions.jsonMapper) + private val extractStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper).mapJson() + + override fun extractStreaming( + params: SessionExtractParams, + requestOptions: RequestOptions, + ): HttpResponseFor> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("id", params.id().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "sessions", params._pathParam(0), "extract") + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("streamResponse", JsonValue.from(true)) + .build(), + ) + ) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .let { extractStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse + } + } + } + } + + private val navigateHandler: Handler = + jsonHandler(clientOptions.jsonMapper) override fun navigate( params: SessionNavigateParams, requestOptions: RequestOptions, - ): HttpResponseFor> { + ): HttpResponseFor { // We check here instead of in the params builder because this can be specified // positionally or in the params class. - checkRequired("sessionId", params.sessionId().getOrNull()) + checkRequired("id", params.id().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) .baseUrl(clientOptions.baseUrl()) - .addPathSegments("sessions", params._pathParam(0), "navigate") + .addPathSegments("v1", "sessions", params._pathParam(0), "navigate") .body(json(clientOptions.jsonMapper, params._body())) .build() .prepare(clientOptions, params) @@ -251,27 +410,27 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO .use { navigateHandler.handle(it) } .also { if (requestOptions.responseValidation!!) { - it.ifPresent { it.validate() } + it.validate() } } } } - private val observeHandler: Handler> = - jsonHandler>(clientOptions.jsonMapper) + private val observeHandler: Handler = + jsonHandler(clientOptions.jsonMapper) override fun observe( params: SessionObserveParams, requestOptions: RequestOptions, - ): HttpResponseFor> { + ): HttpResponseFor { // We check here instead of in the params builder because this can be specified // positionally or in the params class. - checkRequired("sessionId", params.sessionId().getOrNull()) + checkRequired("id", params.id().getOrNull()) val request = HttpRequest.builder() .method(HttpMethod.POST) .baseUrl(clientOptions.baseUrl()) - .addPathSegments("sessions", params._pathParam(0), "observe") + .addPathSegments("v1", "sessions", params._pathParam(0), "observe") .body(json(clientOptions.jsonMapper, params._body())) .build() .prepare(clientOptions, params) @@ -282,7 +441,49 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO .use { observeHandler.handle(it) } .also { if (requestOptions.responseValidation!!) { - it.forEach { it.validate() } + it.validate() + } + } + } + } + + private val observeStreamingHandler: Handler> = + sseHandler(clientOptions.jsonMapper).mapJson() + + override fun observeStreaming( + params: SessionObserveParams, + requestOptions: RequestOptions, + ): HttpResponseFor> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("id", params.id().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "sessions", params._pathParam(0), "observe") + .body( + json( + clientOptions.jsonMapper, + params + ._body() + .toBuilder() + .putAdditionalProperty("streamResponse", JsonValue.from(true)) + .build(), + ) + ) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .let { observeStreamingHandler.handle(it) } + .let { streamResponse -> + if (requestOptions.responseValidation!!) { + streamResponse.map { it.validate() } + } else { + streamResponse } } } @@ -299,7 +500,7 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO HttpRequest.builder() .method(HttpMethod.POST) .baseUrl(clientOptions.baseUrl()) - .addPathSegments("sessions", "start") + .addPathSegments("v1", "sessions", "start") .body(json(clientOptions.jsonMapper, params._body())) .build() .prepare(clientOptions, params) diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/core/handlers/SseHandlerTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/core/handlers/SseHandlerTest.kt new file mode 100644 index 0000000..29105f3 --- /dev/null +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/core/handlers/SseHandlerTest.kt @@ -0,0 +1,134 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.browserbase.api.core.handlers + +import com.browserbase.api.core.http.Headers +import com.browserbase.api.core.http.HttpResponse +import com.browserbase.api.core.http.SseMessage +import com.browserbase.api.core.jsonMapper +import java.io.InputStream +import java.util.stream.Collectors.toList +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.catchThrowable +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class SseHandlerTest { + + enum class TestCase( + internal val body: String, + internal val expectedMessages: List? = null, + internal val expectedException: Exception? = null, + ) { + DATA_MISSING_EVENT( + buildString { + append("data: {\"foo\":true}\n") + append("\n") + }, + listOf(sseMessageBuilder().data("{\"foo\":true}").build()), + ), + MULTIPLE_DATA_MISSING_EVENT( + buildString { + append("data: {\"foo\":true}\n") + append("\n") + append("data: {\"bar\":false}\n") + append("\n") + }, + listOf( + sseMessageBuilder().data("{\"foo\":true}").build(), + sseMessageBuilder().data("{\"bar\":false}").build(), + ), + ), + DATA_JSON_ESCAPED_DOUBLE_NEW_LINE( + buildString { + append("data: {\n") + append("data: \"foo\":\n") + append("data: true}\n") + append("\n\n") + }, + listOf(sseMessageBuilder().data("{\n\"foo\":\ntrue}").build()), + ), + MULTIPLE_DATA_LINES( + buildString { + append("data: {\n") + append("data: \"foo\":\n") + append("data: true}\n") + append("\n\n") + }, + listOf(sseMessageBuilder().data("{\n\"foo\":\ntrue}").build()), + ), + SPECIAL_NEW_LINE_CHARACTER( + buildString { + append("data: {\"content\":\" culpa\"}\n") + append("\n") + append("data: {\"content\":\" \u2028\"}\n") + append("\n") + append("data: {\"content\":\"foo\"}\n") + append("\n") + }, + listOf( + sseMessageBuilder().data("{\"content\":\" culpa\"}").build(), + sseMessageBuilder().data("{\"content\":\" \u2028\"}").build(), + sseMessageBuilder().data("{\"content\":\"foo\"}").build(), + ), + ), + MULTI_BYTE_CHARACTER( + buildString { + append("data: {\"content\":\"\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0438\"}\n") + append("\n") + }, + listOf(sseMessageBuilder().data("{\"content\":\"известни\"}").build()), + ), + } + + @ParameterizedTest + @EnumSource + fun handle(testCase: TestCase) { + val response = httpResponse(testCase.body) + var messages: List? = null + var exception: Exception? = null + + try { + messages = + sseHandler(jsonMapper()).handle(response).use { it.stream().collect(toList()) } + } catch (e: Exception) { + exception = e + } + + if (testCase.expectedMessages != null) { + assertThat(messages).containsExactlyElementsOf(testCase.expectedMessages) + } + if (testCase.expectedException != null) { + assertThat(exception).isInstanceOf(testCase.expectedException.javaClass) + assertThat(exception).hasMessage(testCase.expectedException.message) + } + } + + @Test + fun cannotReuseStream() { + val response = httpResponse("body") + val streamResponse = sseHandler(jsonMapper()).handle(response) + + val throwable = + streamResponse.use { + it.stream().collect(toList()) + catchThrowable { it.stream().collect(toList()) } + } + + assertThat(throwable).isInstanceOf(IllegalStateException::class.java) + } +} + +private fun httpResponse(body: String): HttpResponse = + object : HttpResponse { + override fun statusCode(): Int = 0 + + override fun headers(): Headers = Headers.builder().build() + + override fun body(): InputStream = body.toByteArray().inputStream() + + override fun close() {} + } + +private fun sseMessageBuilder() = SseMessage.builder().jsonMapper(jsonMapper()) diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/core/handlers/StreamHandlerTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/core/handlers/StreamHandlerTest.kt new file mode 100644 index 0000000..7da5378 --- /dev/null +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/core/handlers/StreamHandlerTest.kt @@ -0,0 +1,94 @@ +package com.browserbase.api.core.handlers + +import com.browserbase.api.core.http.Headers +import com.browserbase.api.core.http.HttpResponse +import com.browserbase.api.errors.StagehandIoException +import java.io.IOException +import java.io.InputStream +import kotlin.streams.asSequence +import kotlin.test.Test +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.assertThrows + +internal class StreamHandlerTest { + + @Test + fun streamHandler_splitsStreamOnNewlines() { + val handler = streamHandler { _, lines -> yieldAll(lines) } + val streamResponse = handler.handle(httpResponse("a\nbb\nccc\ndddd".byteInputStream())) + + val lines = streamResponse.stream().asSequence().toList() + + assertThat(lines).containsExactly("a", "bb", "ccc", "dddd") + } + + @Test + fun streamHandler_whenClosedEarly_stopsYielding() { + val handler = streamHandler { _, lines -> yieldAll(lines) } + val streamResponse = handler.handle(httpResponse("a\nbb\nccc\ndddd".byteInputStream())) + + val lines = + streamResponse + .stream() + .asSequence() + .onEach { + if (it == "bb") { + streamResponse.close() + } + } + .toList() + + assertThat(lines).containsExactly("a", "bb") + } + + @Test + fun streamHandler_whenReaderThrowsIOException_wrapsException() { + val handler = streamHandler { _, lines -> lines.forEach {} } + val streamResponse = handler.handle(httpResponse("a\nb\nc\n".byteInputStream().throwing())) + + val e = assertThrows { streamResponse.stream().forEach {} } + assertThat(e).hasMessage("Stream failed") + assertThat(e).hasCauseInstanceOf(IOException::class.java) + } + + @Test + fun streamHandler_whenBlockThrowsIOException_doesNotWrapException() { + val ioException = IOException("BOOM!") + val handler = + streamHandler { _, lines -> + lines.forEachIndexed { index, _ -> + if (index == 2) { + throw ioException + } + } + } + val streamResponse = handler.handle(httpResponse("a\nb\nc\n".byteInputStream())) + + val e = assertThrows { streamResponse.stream().forEach {} } + assertThat(e).isSameAs(ioException) + } + + private fun httpResponse(body: InputStream): HttpResponse = + object : HttpResponse { + + override fun statusCode(): Int = 0 + + override fun headers(): Headers = Headers.builder().build() + + override fun body(): InputStream = body + + override fun close() {} + } + + private fun InputStream.throwing(): InputStream = + object : InputStream() { + + override fun read(): Int { + val byte = this@throwing.read() + if (byte == -1) { + throw IOException("BOOM!") + } + return byte + } + } +} diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/ActionTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/ActionTest.kt index fcd120c..4464f91 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/ActionTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/ActionTest.kt @@ -4,6 +4,7 @@ package com.browserbase.api.models.sessions import com.browserbase.api.core.jsonMapper import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import kotlin.jvm.optionals.getOrNull import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -13,18 +14,16 @@ internal class ActionTest { fun create() { val action = Action.builder() - .addArgument("string") - .description("description") - .method("method") - .selector("selector") - .backendNodeId(0L) + .description("Click the submit button") + .selector("[data-testid='submit-button']") + .addArgument("Hello World") + .method("click") .build() - assertThat(action.arguments()).containsExactly("string") - assertThat(action.description()).isEqualTo("description") - assertThat(action.method()).isEqualTo("method") - assertThat(action.selector()).isEqualTo("selector") - assertThat(action.backendNodeId()).contains(0L) + assertThat(action.description()).isEqualTo("Click the submit button") + assertThat(action.selector()).isEqualTo("[data-testid='submit-button']") + assertThat(action.arguments().getOrNull()).containsExactly("Hello World") + assertThat(action.method()).contains("click") } @Test @@ -32,11 +31,10 @@ internal class ActionTest { val jsonMapper = jsonMapper() val action = Action.builder() - .addArgument("string") - .description("description") - .method("method") - .selector("selector") - .backendNodeId(0L) + .description("Click the submit button") + .selector("[data-testid='submit-button']") + .addArgument("Hello World") + .method("click") .build() val roundtrippedAction = 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 0430aec..d83a01a 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 @@ -2,39 +2,66 @@ 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 internal class ModelConfigTest { @Test - fun create() { - val modelConfig = - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) + fun ofName() { + val name = "openai/gpt-5-nano" + + val modelConfig = ModelConfig.ofName(name) + + assertThat(modelConfig.name()).contains(name) + assertThat(modelConfig.modelConfigObject()).isEmpty + } + + @Test + fun ofNameRoundtrip() { + val jsonMapper = jsonMapper() + val modelConfig = ModelConfig.ofName("openai/gpt-5-nano") + + val roundtrippedModelConfig = + jsonMapper.readValue( + jsonMapper.writeValueAsString(modelConfig), + jacksonTypeRef(), + ) + + assertThat(roundtrippedModelConfig).isEqualTo(modelConfig) + } + + @Test + fun ofModelConfigObject() { + val modelConfigObject = + ModelConfig.ModelConfigObject.builder() + .modelName("gpt-5-nano") + .apiKey("sk-some-openai-api-key") + .baseUrl("https://api.openai.com/v1") .build() - assertThat(modelConfig.apiKey()).contains("apiKey") - assertThat(modelConfig.baseUrl()).contains("https://example.com") - assertThat(modelConfig.model()).contains("model") - assertThat(modelConfig.provider()).contains(ModelConfig.Provider.OPENAI) + val modelConfig = ModelConfig.ofModelConfigObject(modelConfigObject) + + assertThat(modelConfig.name()).isEmpty + assertThat(modelConfig.modelConfigObject()).contains(modelConfigObject) } @Test - fun roundtrip() { + fun ofModelConfigObjectRoundtrip() { val jsonMapper = jsonMapper() val modelConfig = - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() + ModelConfig.ofModelConfigObject( + ModelConfig.ModelConfigObject.builder() + .modelName("gpt-5-nano") + .apiKey("sk-some-openai-api-key") + .baseUrl("https://api.openai.com/v1") + .build() + ) val roundtrippedModelConfig = jsonMapper.readValue( @@ -44,4 +71,13 @@ internal class ModelConfigTest { assertThat(roundtrippedModelConfig).isEqualTo(modelConfig) } + + @Test + fun incompatibleJsonShapeDeserializesToUnknown() { + val value = JsonValue.from(listOf("invalid", "array")) + val modelConfig = jsonMapper().convertValue(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 fbcca00..9c19cb7 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 @@ -4,6 +4,7 @@ package com.browserbase.api.models.sessions import com.browserbase.api.core.JsonValue import com.browserbase.api.core.http.Headers +import java.time.OffsetDateTime import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -12,24 +13,20 @@ internal class SessionActParamsTest { @Test fun create() { SessionActParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionActParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionActParams.XStreamResponse.TRUE) - .input("click the sign in button") + .input("Click the login button") .frameId("frameId") .options( SessionActParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .timeout(0L) + .model("openai/gpt-5-nano") + .timeout(30000.0) .variables( SessionActParams.Options.Variables.builder() - .putAdditionalProperty("foo", JsonValue.from("string")) + .putAdditionalProperty("username", JsonValue.from("john_doe")) .build() ) .build() @@ -41,11 +38,11 @@ internal class SessionActParamsTest { fun pathParams() { val params = SessionActParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") - .input("click the sign in button") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .input("Click the login button") .build() - assertThat(params._pathParam(0)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + assertThat(params._pathParam(0)).isEqualTo("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") // out-of-bound path param assertThat(params._pathParam(1)).isEqualTo("") } @@ -54,24 +51,20 @@ internal class SessionActParamsTest { fun headers() { val params = SessionActParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionActParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionActParams.XStreamResponse.TRUE) - .input("click the sign in button") + .input("Click the login button") .frameId("frameId") .options( SessionActParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .timeout(0L) + .model("openai/gpt-5-nano") + .timeout(30000.0) .variables( SessionActParams.Options.Variables.builder() - .putAdditionalProperty("foo", JsonValue.from("string")) + .putAdditionalProperty("username", JsonValue.from("john_doe")) .build() ) .build() @@ -80,15 +73,23 @@ internal class SessionActParamsTest { val headers = params._headers() - assertThat(headers).isEqualTo(Headers.builder().put("x-stream-response", "true").build()) + assertThat(headers) + .isEqualTo( + Headers.builder() + .put("x-language", "typescript") + .put("x-sdk-version", "3.0.6") + .put("x-sent-at", "2025-01-15T10:30:00Z") + .put("x-stream-response", "true") + .build() + ) } @Test fun headersWithoutOptionalFields() { val params = SessionActParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") - .input("click the sign in button") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .input("Click the login button") .build() val headers = params._headers() @@ -100,24 +101,20 @@ internal class SessionActParamsTest { fun body() { val params = SessionActParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionActParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionActParams.XStreamResponse.TRUE) - .input("click the sign in button") + .input("Click the login button") .frameId("frameId") .options( SessionActParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .timeout(0L) + .model("openai/gpt-5-nano") + .timeout(30000.0) .variables( SessionActParams.Options.Variables.builder() - .putAdditionalProperty("foo", JsonValue.from("string")) + .putAdditionalProperty("username", JsonValue.from("john_doe")) .build() ) .build() @@ -127,23 +124,16 @@ internal class SessionActParamsTest { val body = params._body() assertThat(body.input()) - .isEqualTo(SessionActParams.Input.ofString("click the sign in button")) + .isEqualTo(SessionActParams.Input.ofString("Click the login button")) assertThat(body.frameId()).contains("frameId") assertThat(body.options()) .contains( SessionActParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .timeout(0L) + .model("openai/gpt-5-nano") + .timeout(30000.0) .variables( SessionActParams.Options.Variables.builder() - .putAdditionalProperty("foo", JsonValue.from("string")) + .putAdditionalProperty("username", JsonValue.from("john_doe")) .build() ) .build() @@ -154,13 +144,13 @@ internal class SessionActParamsTest { fun bodyWithoutOptionalFields() { val params = SessionActParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") - .input("click the sign in button") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .input("Click the login button") .build() val body = params._body() assertThat(body.input()) - .isEqualTo(SessionActParams.Input.ofString("click the sign in button")) + .isEqualTo(SessionActParams.Input.ofString("Click the login button")) } } diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActResponseTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActResponseTest.kt index 7f4d8c8..f5e0363 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActResponseTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActResponseTest.kt @@ -13,30 +13,50 @@ internal class SessionActResponseTest { fun create() { val sessionActResponse = SessionActResponse.builder() - .addAction( - Action.builder() - .addArgument("string") - .description("description") - .method("method") - .selector("selector") - .backendNodeId(0L) + .data( + SessionActResponse.Data.builder() + .result( + SessionActResponse.Data.Result.builder() + .actionDescription("Clicked button with text 'Login'") + .addAction( + Action.builder() + .description("Click the submit button") + .selector("[data-testid='submit-button']") + .addArgument("Hello World") + .method("click") + .build() + ) + .message("Successfully clicked the login button") + .success(true) + .build() + ) + .actionId("actionId") .build() ) - .message("message") .success(true) .build() - assertThat(sessionActResponse.actions()) - .containsExactly( - Action.builder() - .addArgument("string") - .description("description") - .method("method") - .selector("selector") - .backendNodeId(0L) + assertThat(sessionActResponse.data()) + .isEqualTo( + SessionActResponse.Data.builder() + .result( + SessionActResponse.Data.Result.builder() + .actionDescription("Clicked button with text 'Login'") + .addAction( + Action.builder() + .description("Click the submit button") + .selector("[data-testid='submit-button']") + .addArgument("Hello World") + .method("click") + .build() + ) + .message("Successfully clicked the login button") + .success(true) + .build() + ) + .actionId("actionId") .build() ) - assertThat(sessionActResponse.message()).isEqualTo("message") assertThat(sessionActResponse.success()).isEqualTo(true) } @@ -45,16 +65,26 @@ internal class SessionActResponseTest { val jsonMapper = jsonMapper() val sessionActResponse = SessionActResponse.builder() - .addAction( - Action.builder() - .addArgument("string") - .description("description") - .method("method") - .selector("selector") - .backendNodeId(0L) + .data( + SessionActResponse.Data.builder() + .result( + SessionActResponse.Data.Result.builder() + .actionDescription("Clicked button with text 'Login'") + .addAction( + Action.builder() + .description("Click the submit button") + .selector("[data-testid='submit-button']") + .addArgument("Hello World") + .method("click") + .build() + ) + .message("Successfully clicked the login button") + .success(true) + .build() + ) + .actionId("actionId") .build() ) - .message("message") .success(true) .build() diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndParamsTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndParamsTest.kt index ab839e5..bc50272 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndParamsTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndParamsTest.kt @@ -2,6 +2,8 @@ package com.browserbase.api.models.sessions +import com.browserbase.api.core.http.Headers +import java.time.OffsetDateTime import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -9,16 +11,54 @@ internal class SessionEndParamsTest { @Test fun create() { - SessionEndParams.builder().sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e").build() + SessionEndParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionEndParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionEndParams.XStreamResponse.TRUE) + .build() } @Test fun pathParams() { - val params = - SessionEndParams.builder().sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e").build() + val params = SessionEndParams.builder().id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123").build() - assertThat(params._pathParam(0)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + assertThat(params._pathParam(0)).isEqualTo("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") // out-of-bound path param assertThat(params._pathParam(1)).isEqualTo("") } + + @Test + fun headers() { + val params = + SessionEndParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionEndParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionEndParams.XStreamResponse.TRUE) + .build() + + val headers = params._headers() + + assertThat(headers) + .isEqualTo( + Headers.builder() + .put("x-language", "typescript") + .put("x-sdk-version", "3.0.6") + .put("x-sent-at", "2025-01-15T10:30:00Z") + .put("x-stream-response", "true") + .build() + ) + } + + @Test + fun headersWithoutOptionalFields() { + val params = SessionEndParams.builder().id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123").build() + + val headers = params._headers() + + assertThat(headers).isEqualTo(Headers.builder().build()) + } } diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndResponseTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndResponseTest.kt index b52414c..041295f 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndResponseTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndResponseTest.kt @@ -13,7 +13,7 @@ internal class SessionEndResponseTest { fun create() { val sessionEndResponse = SessionEndResponse.builder().success(true).build() - assertThat(sessionEndResponse.success()).contains(true) + assertThat(sessionEndResponse.success()).isEqualTo(true) } @Test diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteAgentParamsTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteAgentParamsTest.kt deleted file mode 100644 index 6d0d59b..0000000 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteAgentParamsTest.kt +++ /dev/null @@ -1,170 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. - -package com.browserbase.api.models.sessions - -import com.browserbase.api.core.http.Headers -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - -internal class SessionExecuteAgentParamsTest { - - @Test - fun create() { - SessionExecuteAgentParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") - .xStreamResponse(SessionExecuteAgentParams.XStreamResponse.TRUE) - .agentConfig( - SessionExecuteAgentParams.AgentConfig.builder() - .cua(true) - .model("openai/gpt-4o") - .provider(SessionExecuteAgentParams.AgentConfig.Provider.OPENAI) - .systemPrompt("systemPrompt") - .build() - ) - .executeOptions( - SessionExecuteAgentParams.ExecuteOptions.builder() - .instruction("Find and click the first product") - .highlightCursor(true) - .maxSteps(10L) - .build() - ) - .frameId("frameId") - .build() - } - - @Test - fun pathParams() { - val params = - SessionExecuteAgentParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") - .agentConfig(SessionExecuteAgentParams.AgentConfig.builder().build()) - .executeOptions( - SessionExecuteAgentParams.ExecuteOptions.builder() - .instruction("Find and click the first product") - .build() - ) - .build() - - assertThat(params._pathParam(0)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") - // out-of-bound path param - assertThat(params._pathParam(1)).isEqualTo("") - } - - @Test - fun headers() { - val params = - SessionExecuteAgentParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") - .xStreamResponse(SessionExecuteAgentParams.XStreamResponse.TRUE) - .agentConfig( - SessionExecuteAgentParams.AgentConfig.builder() - .cua(true) - .model("openai/gpt-4o") - .provider(SessionExecuteAgentParams.AgentConfig.Provider.OPENAI) - .systemPrompt("systemPrompt") - .build() - ) - .executeOptions( - SessionExecuteAgentParams.ExecuteOptions.builder() - .instruction("Find and click the first product") - .highlightCursor(true) - .maxSteps(10L) - .build() - ) - .frameId("frameId") - .build() - - val headers = params._headers() - - assertThat(headers).isEqualTo(Headers.builder().put("x-stream-response", "true").build()) - } - - @Test - fun headersWithoutOptionalFields() { - val params = - SessionExecuteAgentParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") - .agentConfig(SessionExecuteAgentParams.AgentConfig.builder().build()) - .executeOptions( - SessionExecuteAgentParams.ExecuteOptions.builder() - .instruction("Find and click the first product") - .build() - ) - .build() - - val headers = params._headers() - - assertThat(headers).isEqualTo(Headers.builder().build()) - } - - @Test - fun body() { - val params = - SessionExecuteAgentParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") - .xStreamResponse(SessionExecuteAgentParams.XStreamResponse.TRUE) - .agentConfig( - SessionExecuteAgentParams.AgentConfig.builder() - .cua(true) - .model("openai/gpt-4o") - .provider(SessionExecuteAgentParams.AgentConfig.Provider.OPENAI) - .systemPrompt("systemPrompt") - .build() - ) - .executeOptions( - SessionExecuteAgentParams.ExecuteOptions.builder() - .instruction("Find and click the first product") - .highlightCursor(true) - .maxSteps(10L) - .build() - ) - .frameId("frameId") - .build() - - val body = params._body() - - assertThat(body.agentConfig()) - .isEqualTo( - SessionExecuteAgentParams.AgentConfig.builder() - .cua(true) - .model("openai/gpt-4o") - .provider(SessionExecuteAgentParams.AgentConfig.Provider.OPENAI) - .systemPrompt("systemPrompt") - .build() - ) - assertThat(body.executeOptions()) - .isEqualTo( - SessionExecuteAgentParams.ExecuteOptions.builder() - .instruction("Find and click the first product") - .highlightCursor(true) - .maxSteps(10L) - .build() - ) - assertThat(body.frameId()).contains("frameId") - } - - @Test - fun bodyWithoutOptionalFields() { - val params = - SessionExecuteAgentParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") - .agentConfig(SessionExecuteAgentParams.AgentConfig.builder().build()) - .executeOptions( - SessionExecuteAgentParams.ExecuteOptions.builder() - .instruction("Find and click the first product") - .build() - ) - .build() - - val body = params._body() - - assertThat(body.agentConfig()) - .isEqualTo(SessionExecuteAgentParams.AgentConfig.builder().build()) - assertThat(body.executeOptions()) - .isEqualTo( - SessionExecuteAgentParams.ExecuteOptions.builder() - .instruction("Find and click the first product") - .build() - ) - } -} diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteAgentResponseTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteAgentResponseTest.kt deleted file mode 100644 index 4737af6..0000000 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteAgentResponseTest.kt +++ /dev/null @@ -1,34 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. - -package com.browserbase.api.models.sessions - -import com.browserbase.api.core.jsonMapper -import com.fasterxml.jackson.module.kotlin.jacksonTypeRef -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - -internal class SessionExecuteAgentResponseTest { - - @Test - fun create() { - val sessionExecuteAgentResponse = - SessionExecuteAgentResponse.builder().message("message").build() - - assertThat(sessionExecuteAgentResponse.message()).contains("message") - } - - @Test - fun roundtrip() { - val jsonMapper = jsonMapper() - val sessionExecuteAgentResponse = - SessionExecuteAgentResponse.builder().message("message").build() - - val roundtrippedSessionExecuteAgentResponse = - jsonMapper.readValue( - jsonMapper.writeValueAsString(sessionExecuteAgentResponse), - jacksonTypeRef(), - ) - - assertThat(roundtrippedSessionExecuteAgentResponse).isEqualTo(sessionExecuteAgentResponse) - } -} 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 new file mode 100644 index 0000000..4de6985 --- /dev/null +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteParamsTest.kt @@ -0,0 +1,199 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.browserbase.api.models.sessions + +import com.browserbase.api.core.http.Headers +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class SessionExecuteParamsTest { + + @Test + fun create() { + SessionExecuteParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionExecuteParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionExecuteParams.XStreamResponse.TRUE) + .agentConfig( + SessionExecuteParams.AgentConfig.builder() + .cua(true) + .model("openai/gpt-5-nano") + .systemPrompt("systemPrompt") + .build() + ) + .executeOptions( + SessionExecuteParams.ExecuteOptions.builder() + .instruction( + "Log in with username 'demo' and password 'test123', then navigate to settings" + ) + .highlightCursor(true) + .maxSteps(20.0) + .build() + ) + .frameId("frameId") + .build() + } + + @Test + fun pathParams() { + val params = + SessionExecuteParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .agentConfig(SessionExecuteParams.AgentConfig.builder().build()) + .executeOptions( + SessionExecuteParams.ExecuteOptions.builder() + .instruction( + "Log in with username 'demo' and password 'test123', then navigate to settings" + ) + .build() + ) + .build() + + assertThat(params._pathParam(0)).isEqualTo("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } + + @Test + fun headers() { + val params = + SessionExecuteParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionExecuteParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionExecuteParams.XStreamResponse.TRUE) + .agentConfig( + SessionExecuteParams.AgentConfig.builder() + .cua(true) + .model("openai/gpt-5-nano") + .systemPrompt("systemPrompt") + .build() + ) + .executeOptions( + SessionExecuteParams.ExecuteOptions.builder() + .instruction( + "Log in with username 'demo' and password 'test123', then navigate to settings" + ) + .highlightCursor(true) + .maxSteps(20.0) + .build() + ) + .frameId("frameId") + .build() + + val headers = params._headers() + + assertThat(headers) + .isEqualTo( + Headers.builder() + .put("x-language", "typescript") + .put("x-sdk-version", "3.0.6") + .put("x-sent-at", "2025-01-15T10:30:00Z") + .put("x-stream-response", "true") + .build() + ) + } + + @Test + fun headersWithoutOptionalFields() { + val params = + SessionExecuteParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .agentConfig(SessionExecuteParams.AgentConfig.builder().build()) + .executeOptions( + SessionExecuteParams.ExecuteOptions.builder() + .instruction( + "Log in with username 'demo' and password 'test123', then navigate to settings" + ) + .build() + ) + .build() + + val headers = params._headers() + + assertThat(headers).isEqualTo(Headers.builder().build()) + } + + @Test + fun body() { + val params = + SessionExecuteParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionExecuteParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionExecuteParams.XStreamResponse.TRUE) + .agentConfig( + SessionExecuteParams.AgentConfig.builder() + .cua(true) + .model("openai/gpt-5-nano") + .systemPrompt("systemPrompt") + .build() + ) + .executeOptions( + SessionExecuteParams.ExecuteOptions.builder() + .instruction( + "Log in with username 'demo' and password 'test123', then navigate to settings" + ) + .highlightCursor(true) + .maxSteps(20.0) + .build() + ) + .frameId("frameId") + .build() + + val body = params._body() + + assertThat(body.agentConfig()) + .isEqualTo( + SessionExecuteParams.AgentConfig.builder() + .cua(true) + .model("openai/gpt-5-nano") + .systemPrompt("systemPrompt") + .build() + ) + assertThat(body.executeOptions()) + .isEqualTo( + SessionExecuteParams.ExecuteOptions.builder() + .instruction( + "Log in with username 'demo' and password 'test123', then navigate to settings" + ) + .highlightCursor(true) + .maxSteps(20.0) + .build() + ) + assertThat(body.frameId()).contains("frameId") + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + SessionExecuteParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .agentConfig(SessionExecuteParams.AgentConfig.builder().build()) + .executeOptions( + SessionExecuteParams.ExecuteOptions.builder() + .instruction( + "Log in with username 'demo' and password 'test123', then navigate to settings" + ) + .build() + ) + .build() + + val body = params._body() + + assertThat(body.agentConfig()).isEqualTo(SessionExecuteParams.AgentConfig.builder().build()) + assertThat(body.executeOptions()) + .isEqualTo( + SessionExecuteParams.ExecuteOptions.builder() + .instruction( + "Log in with username 'demo' and password 'test123', then navigate to settings" + ) + .build() + ) + } +} diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteResponseTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteResponseTest.kt new file mode 100644 index 0000000..85a2099 --- /dev/null +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteResponseTest.kt @@ -0,0 +1,151 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.browserbase.api.models.sessions + +import com.browserbase.api.core.JsonValue +import com.browserbase.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class SessionExecuteResponseTest { + + @Test + fun create() { + val sessionExecuteResponse = + SessionExecuteResponse.builder() + .data( + SessionExecuteResponse.Data.builder() + .result( + SessionExecuteResponse.Data.Result.builder() + .addAction( + SessionExecuteResponse.Data.Result.Action.builder() + .type("click") + .action("action") + .instruction("instruction") + .pageText("pageText") + .pageUrl("pageUrl") + .reasoning("reasoning") + .taskCompleted(true) + .timeMs(0.0) + .build() + ) + .completed(true) + .message("Successfully logged in and navigated to dashboard") + .success(true) + .metadata( + SessionExecuteResponse.Data.Result.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .usage( + SessionExecuteResponse.Data.Result.Usage.builder() + .inferenceTimeMs(2500.0) + .inputTokens(1500.0) + .outputTokens(250.0) + .cachedInputTokens(0.0) + .reasoningTokens(0.0) + .build() + ) + .build() + ) + .build() + ) + .success(true) + .build() + + assertThat(sessionExecuteResponse.data()) + .isEqualTo( + SessionExecuteResponse.Data.builder() + .result( + SessionExecuteResponse.Data.Result.builder() + .addAction( + SessionExecuteResponse.Data.Result.Action.builder() + .type("click") + .action("action") + .instruction("instruction") + .pageText("pageText") + .pageUrl("pageUrl") + .reasoning("reasoning") + .taskCompleted(true) + .timeMs(0.0) + .build() + ) + .completed(true) + .message("Successfully logged in and navigated to dashboard") + .success(true) + .metadata( + SessionExecuteResponse.Data.Result.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .usage( + SessionExecuteResponse.Data.Result.Usage.builder() + .inferenceTimeMs(2500.0) + .inputTokens(1500.0) + .outputTokens(250.0) + .cachedInputTokens(0.0) + .reasoningTokens(0.0) + .build() + ) + .build() + ) + .build() + ) + assertThat(sessionExecuteResponse.success()).isEqualTo(true) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val sessionExecuteResponse = + SessionExecuteResponse.builder() + .data( + SessionExecuteResponse.Data.builder() + .result( + SessionExecuteResponse.Data.Result.builder() + .addAction( + SessionExecuteResponse.Data.Result.Action.builder() + .type("click") + .action("action") + .instruction("instruction") + .pageText("pageText") + .pageUrl("pageUrl") + .reasoning("reasoning") + .taskCompleted(true) + .timeMs(0.0) + .build() + ) + .completed(true) + .message("Successfully logged in and navigated to dashboard") + .success(true) + .metadata( + SessionExecuteResponse.Data.Result.Metadata.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .usage( + SessionExecuteResponse.Data.Result.Usage.builder() + .inferenceTimeMs(2500.0) + .inputTokens(1500.0) + .outputTokens(250.0) + .cachedInputTokens(0.0) + .reasoningTokens(0.0) + .build() + ) + .build() + ) + .build() + ) + .success(true) + .build() + + val roundtrippedSessionExecuteResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(sessionExecuteResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedSessionExecuteResponse).isEqualTo(sessionExecuteResponse) + } +} 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 7df72a7..b8d0a00 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 @@ -4,6 +4,7 @@ package com.browserbase.api.models.sessions import com.browserbase.api.core.JsonValue import com.browserbase.api.core.http.Headers +import java.time.OffsetDateTime import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -12,22 +13,18 @@ internal class SessionExtractParamsTest { @Test fun create() { SessionExtractParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionExtractParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionExtractParams.XStreamResponse.TRUE) .frameId("frameId") - .instruction("extract the page title") + .instruction("Extract all product names and prices from the page") .options( SessionExtractParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .selector("selector") - .timeout(0L) + .model("openai/gpt-5-nano") + .selector("#main-content") + .timeout(30000.0) .build() ) .schema( @@ -41,9 +38,9 @@ internal class SessionExtractParamsTest { @Test fun pathParams() { val params = - SessionExtractParams.builder().sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e").build() + SessionExtractParams.builder().id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123").build() - assertThat(params._pathParam(0)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + assertThat(params._pathParam(0)).isEqualTo("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") // out-of-bound path param assertThat(params._pathParam(1)).isEqualTo("") } @@ -52,22 +49,18 @@ internal class SessionExtractParamsTest { fun headers() { val params = SessionExtractParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionExtractParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionExtractParams.XStreamResponse.TRUE) .frameId("frameId") - .instruction("extract the page title") + .instruction("Extract all product names and prices from the page") .options( SessionExtractParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .selector("selector") - .timeout(0L) + .model("openai/gpt-5-nano") + .selector("#main-content") + .timeout(30000.0) .build() ) .schema( @@ -79,13 +72,21 @@ internal class SessionExtractParamsTest { val headers = params._headers() - assertThat(headers).isEqualTo(Headers.builder().put("x-stream-response", "true").build()) + assertThat(headers) + .isEqualTo( + Headers.builder() + .put("x-language", "typescript") + .put("x-sdk-version", "3.0.6") + .put("x-sent-at", "2025-01-15T10:30:00Z") + .put("x-stream-response", "true") + .build() + ) } @Test fun headersWithoutOptionalFields() { val params = - SessionExtractParams.builder().sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e").build() + SessionExtractParams.builder().id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123").build() val headers = params._headers() @@ -96,22 +97,18 @@ internal class SessionExtractParamsTest { fun body() { val params = SessionExtractParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionExtractParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionExtractParams.XStreamResponse.TRUE) .frameId("frameId") - .instruction("extract the page title") + .instruction("Extract all product names and prices from the page") .options( SessionExtractParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .selector("selector") - .timeout(0L) + .model("openai/gpt-5-nano") + .selector("#main-content") + .timeout(30000.0) .build() ) .schema( @@ -124,20 +121,14 @@ internal class SessionExtractParamsTest { val body = params._body() assertThat(body.frameId()).contains("frameId") - assertThat(body.instruction()).contains("extract the page title") + assertThat(body.instruction()) + .contains("Extract all product names and prices from the page") assertThat(body.options()) .contains( SessionExtractParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .selector("selector") - .timeout(0L) + .model("openai/gpt-5-nano") + .selector("#main-content") + .timeout(30000.0) .build() ) assertThat(body.schema()) @@ -151,7 +142,7 @@ internal class SessionExtractParamsTest { @Test fun bodyWithoutOptionalFields() { val params = - SessionExtractParams.builder().sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e").build() + SessionExtractParams.builder().id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123").build() val body = params._body() } diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractResponseTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractResponseTest.kt index 42022d5..3f055cd 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractResponseTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractResponseTest.kt @@ -4,66 +4,48 @@ 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 SessionExtractResponseTest { @Test - fun ofExtraction() { - val extraction = - SessionExtractResponse.Extraction.builder().extraction("extraction").build() - - val sessionExtractResponse = SessionExtractResponse.ofExtraction(extraction) - - assertThat(sessionExtractResponse.extraction()).contains(extraction) - assertThat(sessionExtractResponse.custom()).isEmpty - } - - @Test - fun ofExtractionRoundtrip() { - val jsonMapper = jsonMapper() + fun create() { val sessionExtractResponse = - SessionExtractResponse.ofExtraction( - SessionExtractResponse.Extraction.builder().extraction("extraction").build() - ) - - val roundtrippedSessionExtractResponse = - jsonMapper.readValue( - jsonMapper.writeValueAsString(sessionExtractResponse), - jacksonTypeRef(), - ) - - assertThat(roundtrippedSessionExtractResponse).isEqualTo(sessionExtractResponse) - } - - @Test - fun ofCustom() { - val custom = - SessionExtractResponse.Custom.builder() - .putAdditionalProperty("foo", JsonValue.from("bar")) + SessionExtractResponse.builder() + .data( + SessionExtractResponse.Data.builder() + .result(JsonValue.from(mapOf())) + .actionId("actionId") + .build() + ) + .success(true) .build() - val sessionExtractResponse = SessionExtractResponse.ofCustom(custom) - - assertThat(sessionExtractResponse.extraction()).isEmpty - assertThat(sessionExtractResponse.custom()).contains(custom) + assertThat(sessionExtractResponse.data()) + .isEqualTo( + SessionExtractResponse.Data.builder() + .result(JsonValue.from(mapOf())) + .actionId("actionId") + .build() + ) + assertThat(sessionExtractResponse.success()).isEqualTo(true) } @Test - fun ofCustomRoundtrip() { + fun roundtrip() { val jsonMapper = jsonMapper() val sessionExtractResponse = - SessionExtractResponse.ofCustom( - SessionExtractResponse.Custom.builder() - .putAdditionalProperty("foo", JsonValue.from("bar")) - .build() - ) + SessionExtractResponse.builder() + .data( + SessionExtractResponse.Data.builder() + .result(JsonValue.from(mapOf())) + .actionId("actionId") + .build() + ) + .success(true) + .build() val roundtrippedSessionExtractResponse = jsonMapper.readValue( @@ -73,22 +55,4 @@ internal class SessionExtractResponseTest { assertThat(roundtrippedSessionExtractResponse).isEqualTo(sessionExtractResponse) } - - 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 sessionExtractResponse = - jsonMapper().convertValue(testCase.value, jacksonTypeRef()) - - val e = assertThrows { sessionExtractResponse.validate() } - assertThat(e).hasMessageStartingWith("Unknown ") - } } diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateParamsTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateParamsTest.kt index fe762db..169d089 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateParamsTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateParamsTest.kt @@ -3,6 +3,7 @@ package com.browserbase.api.models.sessions import com.browserbase.api.core.http.Headers +import java.time.OffsetDateTime import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -11,15 +12,21 @@ internal class SessionNavigateParamsTest { @Test fun create() { SessionNavigateParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionNavigateParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionNavigateParams.XStreamResponse.TRUE) .url("https://example.com") .frameId("frameId") .options( SessionNavigateParams.Options.builder() - .waitUntil(SessionNavigateParams.Options.WaitUntil.LOAD) + .referer("referer") + .timeout(30000.0) + .waitUntil(SessionNavigateParams.Options.WaitUntil.NETWORKIDLE) .build() ) + .streamResponse(true) .build() } @@ -27,11 +34,11 @@ internal class SessionNavigateParamsTest { fun pathParams() { val params = SessionNavigateParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .url("https://example.com") .build() - assertThat(params._pathParam(0)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + assertThat(params._pathParam(0)).isEqualTo("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") // out-of-bound path param assertThat(params._pathParam(1)).isEqualTo("") } @@ -40,27 +47,41 @@ internal class SessionNavigateParamsTest { fun headers() { val params = SessionNavigateParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionNavigateParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionNavigateParams.XStreamResponse.TRUE) .url("https://example.com") .frameId("frameId") .options( SessionNavigateParams.Options.builder() - .waitUntil(SessionNavigateParams.Options.WaitUntil.LOAD) + .referer("referer") + .timeout(30000.0) + .waitUntil(SessionNavigateParams.Options.WaitUntil.NETWORKIDLE) .build() ) + .streamResponse(true) .build() val headers = params._headers() - assertThat(headers).isEqualTo(Headers.builder().put("x-stream-response", "true").build()) + assertThat(headers) + .isEqualTo( + Headers.builder() + .put("x-language", "typescript") + .put("x-sdk-version", "3.0.6") + .put("x-sent-at", "2025-01-15T10:30:00Z") + .put("x-stream-response", "true") + .build() + ) } @Test fun headersWithoutOptionalFields() { val params = SessionNavigateParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .url("https://example.com") .build() @@ -73,15 +94,21 @@ internal class SessionNavigateParamsTest { fun body() { val params = SessionNavigateParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionNavigateParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionNavigateParams.XStreamResponse.TRUE) .url("https://example.com") .frameId("frameId") .options( SessionNavigateParams.Options.builder() - .waitUntil(SessionNavigateParams.Options.WaitUntil.LOAD) + .referer("referer") + .timeout(30000.0) + .waitUntil(SessionNavigateParams.Options.WaitUntil.NETWORKIDLE) .build() ) + .streamResponse(true) .build() val body = params._body() @@ -91,16 +118,19 @@ internal class SessionNavigateParamsTest { assertThat(body.options()) .contains( SessionNavigateParams.Options.builder() - .waitUntil(SessionNavigateParams.Options.WaitUntil.LOAD) + .referer("referer") + .timeout(30000.0) + .waitUntil(SessionNavigateParams.Options.WaitUntil.NETWORKIDLE) .build() ) + assertThat(body.streamResponse()).contains(true) } @Test fun bodyWithoutOptionalFields() { val params = SessionNavigateParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .url("https://example.com") .build() diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponseTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponseTest.kt index 1385964..bca29d9 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponseTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponseTest.kt @@ -2,6 +2,7 @@ package com.browserbase.api.models.sessions +import com.browserbase.api.core.JsonValue import com.browserbase.api.core.jsonMapper import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import org.assertj.core.api.Assertions.assertThat @@ -12,18 +13,39 @@ internal class SessionNavigateResponseTest { @Test fun create() { val sessionNavigateResponse = - SessionNavigateResponse.builder().ok(true).status(0L).url("url").build() - - assertThat(sessionNavigateResponse.ok()).contains(true) - assertThat(sessionNavigateResponse.status()).contains(0L) - assertThat(sessionNavigateResponse.url()).contains("url") + SessionNavigateResponse.builder() + .data( + SessionNavigateResponse.Data.builder() + .result(JsonValue.from(mapOf())) + .actionId("actionId") + .build() + ) + .success(true) + .build() + + assertThat(sessionNavigateResponse.data()) + .isEqualTo( + SessionNavigateResponse.Data.builder() + .result(JsonValue.from(mapOf())) + .actionId("actionId") + .build() + ) + assertThat(sessionNavigateResponse.success()).isEqualTo(true) } @Test fun roundtrip() { val jsonMapper = jsonMapper() val sessionNavigateResponse = - SessionNavigateResponse.builder().ok(true).status(0L).url("url").build() + SessionNavigateResponse.builder() + .data( + SessionNavigateResponse.Data.builder() + .result(JsonValue.from(mapOf())) + .actionId("actionId") + .build() + ) + .success(true) + .build() val roundtrippedSessionNavigateResponse = jsonMapper.readValue( 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 a8ab677..db03460 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 @@ -3,6 +3,7 @@ package com.browserbase.api.models.sessions import com.browserbase.api.core.http.Headers +import java.time.OffsetDateTime import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -11,22 +12,18 @@ internal class SessionObserveParamsTest { @Test fun create() { SessionObserveParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionObserveParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionObserveParams.XStreamResponse.TRUE) .frameId("frameId") - .instruction("instruction") + .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .selector("selector") - .timeout(0L) + .model("openai/gpt-5-nano") + .selector("nav") + .timeout(30000.0) .build() ) .build() @@ -35,9 +32,9 @@ internal class SessionObserveParamsTest { @Test fun pathParams() { val params = - SessionObserveParams.builder().sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e").build() + SessionObserveParams.builder().id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123").build() - assertThat(params._pathParam(0)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + assertThat(params._pathParam(0)).isEqualTo("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") // out-of-bound path param assertThat(params._pathParam(1)).isEqualTo("") } @@ -46,35 +43,39 @@ internal class SessionObserveParamsTest { fun headers() { val params = SessionObserveParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionObserveParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionObserveParams.XStreamResponse.TRUE) .frameId("frameId") - .instruction("instruction") + .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .selector("selector") - .timeout(0L) + .model("openai/gpt-5-nano") + .selector("nav") + .timeout(30000.0) .build() ) .build() val headers = params._headers() - assertThat(headers).isEqualTo(Headers.builder().put("x-stream-response", "true").build()) + assertThat(headers) + .isEqualTo( + Headers.builder() + .put("x-language", "typescript") + .put("x-sdk-version", "3.0.6") + .put("x-sent-at", "2025-01-15T10:30:00Z") + .put("x-stream-response", "true") + .build() + ) } @Test fun headersWithoutOptionalFields() { val params = - SessionObserveParams.builder().sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e").build() + SessionObserveParams.builder().id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123").build() val headers = params._headers() @@ -85,22 +86,18 @@ internal class SessionObserveParamsTest { fun body() { val params = SessionObserveParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionObserveParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionObserveParams.XStreamResponse.TRUE) .frameId("frameId") - .instruction("instruction") + .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .selector("selector") - .timeout(0L) + .model("openai/gpt-5-nano") + .selector("nav") + .timeout(30000.0) .build() ) .build() @@ -108,20 +105,13 @@ internal class SessionObserveParamsTest { val body = params._body() assertThat(body.frameId()).contains("frameId") - assertThat(body.instruction()).contains("instruction") + assertThat(body.instruction()).contains("Find all clickable navigation links") assertThat(body.options()) .contains( SessionObserveParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .selector("selector") - .timeout(0L) + .model("openai/gpt-5-nano") + .selector("nav") + .timeout(30000.0) .build() ) } @@ -129,7 +119,7 @@ internal class SessionObserveParamsTest { @Test fun bodyWithoutOptionalFields() { val params = - SessionObserveParams.builder().sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e").build() + SessionObserveParams.builder().id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123").build() val body = params._body() } diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveResponseTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveResponseTest.kt new file mode 100644 index 0000000..2cc31b8 --- /dev/null +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveResponseTest.kt @@ -0,0 +1,78 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.browserbase.api.models.sessions + +import com.browserbase.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class SessionObserveResponseTest { + + @Test + fun create() { + val sessionObserveResponse = + SessionObserveResponse.builder() + .data( + SessionObserveResponse.Data.builder() + .addResult( + Action.builder() + .description("Click the submit button") + .selector("[data-testid='submit-button']") + .addArgument("Hello World") + .method("click") + .build() + ) + .actionId("actionId") + .build() + ) + .success(true) + .build() + + assertThat(sessionObserveResponse.data()) + .isEqualTo( + SessionObserveResponse.Data.builder() + .addResult( + Action.builder() + .description("Click the submit button") + .selector("[data-testid='submit-button']") + .addArgument("Hello World") + .method("click") + .build() + ) + .actionId("actionId") + .build() + ) + assertThat(sessionObserveResponse.success()).isEqualTo(true) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val sessionObserveResponse = + SessionObserveResponse.builder() + .data( + SessionObserveResponse.Data.builder() + .addResult( + Action.builder() + .description("Click the submit button") + .selector("[data-testid='submit-button']") + .addArgument("Hello World") + .method("click") + .build() + ) + .actionId("actionId") + .build() + ) + .success(true) + .build() + + val roundtrippedSessionObserveResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(sessionObserveResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedSessionObserveResponse).isEqualTo(sessionObserveResponse) + } +} diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartParamsTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartParamsTest.kt index 9454bf3..56f4736 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartParamsTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartParamsTest.kt @@ -2,6 +2,9 @@ package com.browserbase.api.models.sessions +import com.browserbase.api.core.JsonValue +import com.browserbase.api.core.http.Headers +import java.time.OffsetDateTime import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -10,51 +13,627 @@ internal class SessionStartParamsTest { @Test fun create() { SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport.builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams.BrowserSettings.builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams.BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams.BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams.BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region(SessionStartParams.BrowserbaseSessionCreateParams.Region.US_WEST_2) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() } + @Test + fun headers() { + val params = + SessionStartParams.builder() + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport.builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams.BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region(SessionStartParams.BrowserbaseSessionCreateParams.Region.US_WEST_2) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) + .selfHeal(true) + .systemPrompt("systemPrompt") + .verbose(1L) + .waitForCaptchaSolves(true) + .build() + + val headers = params._headers() + + assertThat(headers) + .isEqualTo( + Headers.builder() + .put("x-language", "typescript") + .put("x-sdk-version", "3.0.6") + .put("x-sent-at", "2025-01-15T10:30:00Z") + .put("x-stream-response", "true") + .build() + ) + } + + @Test + fun headersWithoutOptionalFields() { + val params = SessionStartParams.builder().modelName("gpt-4o").build() + + val headers = params._headers() + + assertThat(headers).isEqualTo(Headers.builder().build()) + } + @Test fun body() { val params = SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport.builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams.BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region(SessionStartParams.BrowserbaseSessionCreateParams.Region.US_WEST_2) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() val body = params._body() - assertThat(body.browserbaseApiKey()).isEqualTo("BROWSERBASE_API_KEY") - assertThat(body.browserbaseProjectId()).isEqualTo("BROWSERBASE_PROJECT_ID") - assertThat(body.domSettleTimeout()).contains(0L) - assertThat(body.model()).contains("openai/gpt-4o") + assertThat(body.modelName()).isEqualTo("gpt-4o") + assertThat(body.actTimeoutMs()).contains(30000.0) + assertThat(body.browser()) + .contains( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport.builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + assertThat(body.browserbaseSessionCreateParams()) + .contains( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams.BrowserSettings.builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams.BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams.BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams.BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region(SessionStartParams.BrowserbaseSessionCreateParams.Region.US_WEST_2) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + assertThat(body.browserbaseSessionId()).contains("browserbaseSessionID") + assertThat(body.debugDom()).contains(true) + assertThat(body.domSettleTimeoutMs()).contains(5000.0) + assertThat(body.experimental()).contains(true) assertThat(body.selfHeal()).contains(true) assertThat(body.systemPrompt()).contains("systemPrompt") assertThat(body.verbose()).contains(1L) + assertThat(body.waitForCaptchaSolves()).contains(true) } @Test fun bodyWithoutOptionalFields() { - val params = - SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .build() + val params = SessionStartParams.builder().modelName("gpt-4o").build() val body = params._body() - assertThat(body.browserbaseApiKey()).isEqualTo("BROWSERBASE_API_KEY") - assertThat(body.browserbaseProjectId()).isEqualTo("BROWSERBASE_PROJECT_ID") + assertThat(body.modelName()).isEqualTo("gpt-4o") } } diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartResponseTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartResponseTest.kt index 714c73d..4a712ec 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartResponseTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartResponseTest.kt @@ -13,13 +13,25 @@ internal class SessionStartResponseTest { fun create() { val sessionStartResponse = SessionStartResponse.builder() - .available(true) - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .data( + SessionStartResponse.Data.builder() + .available(true) + .connectUrl("wss://connect.browserbase.com/?signingKey=abc123") + .sessionId("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .build() + ) + .success(true) .build() - assertThat(sessionStartResponse.available()).isEqualTo(true) - assertThat(sessionStartResponse.sessionId()) - .isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + assertThat(sessionStartResponse.data()) + .isEqualTo( + SessionStartResponse.Data.builder() + .available(true) + .connectUrl("wss://connect.browserbase.com/?signingKey=abc123") + .sessionId("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .build() + ) + assertThat(sessionStartResponse.success()).isEqualTo(true) } @Test @@ -27,8 +39,14 @@ internal class SessionStartResponseTest { val jsonMapper = jsonMapper() val sessionStartResponse = SessionStartResponse.builder() - .available(true) - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .data( + SessionStartResponse.Data.builder() + .available(true) + .connectUrl("wss://connect.browserbase.com/?signingKey=abc123") + .sessionId("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .build() + ) + .success(true) .build() val roundtrippedSessionStartResponse = diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/StreamEventTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/StreamEventTest.kt new file mode 100644 index 0000000..9d58bf8 --- /dev/null +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/StreamEventTest.kt @@ -0,0 +1,66 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.browserbase.api.models.sessions + +import com.browserbase.api.core.JsonValue +import com.browserbase.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class StreamEventTest { + + @Test + fun create() { + val streamEvent = + StreamEvent.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .data( + StreamEvent.Data.StreamEventSystemDataOutput.builder() + .status(StreamEvent.Data.StreamEventSystemDataOutput.Status.STARTING) + .error("error") + .result(JsonValue.from(mapOf())) + .build() + ) + .type(StreamEvent.Type.SYSTEM) + .build() + + assertThat(streamEvent.id()).isEqualTo("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + assertThat(streamEvent.data()) + .isEqualTo( + StreamEvent.Data.ofStreamEventSystemDataOutput( + StreamEvent.Data.StreamEventSystemDataOutput.builder() + .status(StreamEvent.Data.StreamEventSystemDataOutput.Status.STARTING) + .error("error") + .result(JsonValue.from(mapOf())) + .build() + ) + ) + assertThat(streamEvent.type()).isEqualTo(StreamEvent.Type.SYSTEM) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val streamEvent = + StreamEvent.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .data( + StreamEvent.Data.StreamEventSystemDataOutput.builder() + .status(StreamEvent.Data.StreamEventSystemDataOutput.Status.STARTING) + .error("error") + .result(JsonValue.from(mapOf())) + .build() + ) + .type(StreamEvent.Type.SYSTEM) + .build() + + val roundtrippedStreamEvent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(streamEvent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedStreamEvent).isEqualTo(streamEvent) + } +} diff --git a/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/ErrorHandlingTest.kt b/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/ErrorHandlingTest.kt index 509b889..e453d81 100644 --- a/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/ErrorHandlingTest.kt +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/services/ErrorHandlingTest.kt @@ -23,6 +23,7 @@ import com.github.tomakehurst.wiremock.client.WireMock.status import com.github.tomakehurst.wiremock.client.WireMock.stubFor import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo import com.github.tomakehurst.wiremock.junit5.WireMockTest +import java.time.OffsetDateTime import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.entry import org.junit.jupiter.api.BeforeEach @@ -74,13 +75,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -104,13 +255,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -134,13 +435,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -164,13 +615,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -194,13 +795,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -224,13 +975,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -254,13 +1155,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -284,13 +1335,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -314,13 +1515,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -344,13 +1695,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -374,13 +1875,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -404,13 +2055,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -434,13 +2235,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -464,13 +2415,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -494,13 +2595,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -524,13 +2775,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) } @@ -552,13 +2953,163 @@ internal class ErrorHandlingTest { assertThrows { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams + .BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region + .US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .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 aa39440..0cdc99d 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 @@ -18,6 +17,7 @@ import com.github.tomakehurst.wiremock.client.WireMock.stubFor import com.github.tomakehurst.wiremock.client.WireMock.verify import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo import com.github.tomakehurst.wiremock.junit5.WireMockTest +import java.time.OffsetDateTime import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test @@ -48,13 +48,152 @@ internal class ServiceParamsTest { sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport.builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams.BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region(SessionStartParams.BrowserbaseSessionCreateParams.Region.US_WEST_2) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .putAdditionalHeader("Secret-Header", "42") .putAdditionalQueryParam("secret_query_param", "42") .putAdditionalBodyProperty("secretProperty", JsonValue.from("42")) @@ -77,24 +216,20 @@ internal class ServiceParamsTest { sessionService.act( SessionActParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionActParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionActParams.XStreamResponse.TRUE) - .input("click the sign in button") + .input("Click the login button") .frameId("frameId") .options( SessionActParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .timeout(0L) + .model("openai/gpt-5-nano") + .timeout(30000.0) .variables( SessionActParams.Options.Variables.builder() - .putAdditionalProperty("foo", JsonValue.from("string")) + .putAdditionalProperty("username", JsonValue.from("john_doe")) .build() ) .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 d041842..78ba6e7 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 @@ -5,14 +5,14 @@ package com.browserbase.api.services.async import com.browserbase.api.TestServerExtension 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.SessionExecuteAgentParams +import com.browserbase.api.models.sessions.SessionEndParams +import com.browserbase.api.models.sessions.SessionExecuteParams import com.browserbase.api.models.sessions.SessionExtractParams import com.browserbase.api.models.sessions.SessionNavigateParams import com.browserbase.api.models.sessions.SessionObserveParams import com.browserbase.api.models.sessions.SessionStartParams -import kotlin.jvm.optionals.getOrNull +import java.time.OffsetDateTime import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -35,24 +35,20 @@ internal class SessionServiceAsyncTest { val responseFuture = sessionServiceAsync.act( SessionActParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionActParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionActParams.XStreamResponse.TRUE) - .input("click the sign in button") + .input("Click the login button") .frameId("frameId") .options( SessionActParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .timeout(0L) + .model("openai/gpt-5-nano") + .timeout(30000.0) .variables( SessionActParams.Options.Variables.builder() - .putAdditionalProperty("foo", JsonValue.from("string")) + .putAdditionalProperty("username", JsonValue.from("john_doe")) .build() ) .build() @@ -64,6 +60,47 @@ internal class SessionServiceAsyncTest { response.validate() } + @Disabled("Prism tests are disabled") + @Test + fun actStreaming() { + val client = + StagehandOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .browserbaseApiKey("My Browserbase API Key") + .browserbaseProjectId("My Browserbase Project ID") + .modelApiKey("My Model API Key") + .build() + val sessionServiceAsync = client.sessions() + + val responseStreamResponse = + sessionServiceAsync.actStreaming( + SessionActParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionActParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionActParams.XStreamResponse.TRUE) + .input("Click the login button") + .frameId("frameId") + .options( + SessionActParams.Options.builder() + .model("openai/gpt-5-nano") + .timeout(30000.0) + .variables( + SessionActParams.Options.Variables.builder() + .putAdditionalProperty("username", JsonValue.from("john_doe")) + .build() + ) + .build() + ) + .build() + ) + + val onCompleteFuture = + responseStreamResponse.subscribe { response -> response.validate() }.onCompleteFuture() + onCompleteFuture.get() + } + @Disabled("Prism tests are disabled") @Test fun end() { @@ -76,7 +113,16 @@ internal class SessionServiceAsyncTest { .build() val sessionServiceAsync = client.sessions() - val responseFuture = sessionServiceAsync.end("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + val responseFuture = + sessionServiceAsync.end( + SessionEndParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionEndParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionEndParams.XStreamResponse.TRUE) + .build() + ) val response = responseFuture.get() response.validate() @@ -84,7 +130,7 @@ internal class SessionServiceAsyncTest { @Disabled("Prism tests are disabled") @Test - fun executeAgent() { + fun execute() { val client = StagehandOkHttpClientAsync.builder() .baseUrl(TestServerExtension.BASE_URL) @@ -95,23 +141,27 @@ internal class SessionServiceAsyncTest { val sessionServiceAsync = client.sessions() val responseFuture = - sessionServiceAsync.executeAgent( - SessionExecuteAgentParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") - .xStreamResponse(SessionExecuteAgentParams.XStreamResponse.TRUE) + sessionServiceAsync.execute( + SessionExecuteParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionExecuteParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionExecuteParams.XStreamResponse.TRUE) .agentConfig( - SessionExecuteAgentParams.AgentConfig.builder() + SessionExecuteParams.AgentConfig.builder() .cua(true) - .model("openai/gpt-4o") - .provider(SessionExecuteAgentParams.AgentConfig.Provider.OPENAI) + .model("openai/gpt-5-nano") .systemPrompt("systemPrompt") .build() ) .executeOptions( - SessionExecuteAgentParams.ExecuteOptions.builder() - .instruction("Find and click the first product") + SessionExecuteParams.ExecuteOptions.builder() + .instruction( + "Log in with username 'demo' and password 'test123', then navigate to settings" + ) .highlightCursor(true) - .maxSteps(10L) + .maxSteps(20.0) .build() ) .frameId("frameId") @@ -122,6 +172,51 @@ internal class SessionServiceAsyncTest { response.validate() } + @Disabled("Prism tests are disabled") + @Test + fun executeStreaming() { + val client = + StagehandOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .browserbaseApiKey("My Browserbase API Key") + .browserbaseProjectId("My Browserbase Project ID") + .modelApiKey("My Model API Key") + .build() + val sessionServiceAsync = client.sessions() + + val responseStreamResponse = + sessionServiceAsync.executeStreaming( + SessionExecuteParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionExecuteParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionExecuteParams.XStreamResponse.TRUE) + .agentConfig( + SessionExecuteParams.AgentConfig.builder() + .cua(true) + .model("openai/gpt-5-nano") + .systemPrompt("systemPrompt") + .build() + ) + .executeOptions( + SessionExecuteParams.ExecuteOptions.builder() + .instruction( + "Log in with username 'demo' and password 'test123', then navigate to settings" + ) + .highlightCursor(true) + .maxSteps(20.0) + .build() + ) + .frameId("frameId") + .build() + ) + + val onCompleteFuture = + responseStreamResponse.subscribe { response -> response.validate() }.onCompleteFuture() + onCompleteFuture.get() + } + @Disabled("Prism tests are disabled") @Test fun extract() { @@ -137,22 +232,18 @@ internal class SessionServiceAsyncTest { val responseFuture = sessionServiceAsync.extract( SessionExtractParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionExtractParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionExtractParams.XStreamResponse.TRUE) .frameId("frameId") - .instruction("extract the page title") + .instruction("Extract all product names and prices from the page") .options( SessionExtractParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .selector("selector") - .timeout(0L) + .model("openai/gpt-5-nano") + .selector("#main-content") + .timeout(30000.0) .build() ) .schema( @@ -167,6 +258,48 @@ internal class SessionServiceAsyncTest { response.validate() } + @Disabled("Prism tests are disabled") + @Test + fun extractStreaming() { + val client = + StagehandOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .browserbaseApiKey("My Browserbase API Key") + .browserbaseProjectId("My Browserbase Project ID") + .modelApiKey("My Model API Key") + .build() + val sessionServiceAsync = client.sessions() + + val responseStreamResponse = + sessionServiceAsync.extractStreaming( + SessionExtractParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionExtractParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionExtractParams.XStreamResponse.TRUE) + .frameId("frameId") + .instruction("Extract all product names and prices from the page") + .options( + SessionExtractParams.Options.builder() + .model("openai/gpt-5-nano") + .selector("#main-content") + .timeout(30000.0) + .build() + ) + .schema( + SessionExtractParams.Schema.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + + val onCompleteFuture = + responseStreamResponse.subscribe { response -> response.validate() }.onCompleteFuture() + onCompleteFuture.get() + } + @Disabled("Prism tests are disabled") @Test fun navigate() { @@ -182,21 +315,26 @@ internal class SessionServiceAsyncTest { val responseFuture = sessionServiceAsync.navigate( SessionNavigateParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionNavigateParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionNavigateParams.XStreamResponse.TRUE) .url("https://example.com") .frameId("frameId") .options( SessionNavigateParams.Options.builder() - .waitUntil(SessionNavigateParams.Options.WaitUntil.LOAD) + .referer("referer") + .timeout(30000.0) + .waitUntil(SessionNavigateParams.Options.WaitUntil.NETWORKIDLE) .build() ) + .streamResponse(true) .build() ) val response = responseFuture.get() - val unwrappedResponse = response.getOrNull() - unwrappedResponse?.validate() + response.validate() } @Disabled("Prism tests are disabled") @@ -211,32 +349,65 @@ internal class SessionServiceAsyncTest { .build() val sessionServiceAsync = client.sessions() - val actionsFuture = + val responseFuture = sessionServiceAsync.observe( SessionObserveParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionObserveParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionObserveParams.XStreamResponse.TRUE) .frameId("frameId") - .instruction("instruction") + .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .selector("selector") - .timeout(0L) + .model("openai/gpt-5-nano") + .selector("nav") + .timeout(30000.0) .build() ) .build() ) - val actions = actionsFuture.get() - actions.forEach { it.validate() } + val response = responseFuture.get() + response.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun observeStreaming() { + val client = + StagehandOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .browserbaseApiKey("My Browserbase API Key") + .browserbaseProjectId("My Browserbase Project ID") + .modelApiKey("My Model API Key") + .build() + val sessionServiceAsync = client.sessions() + + val responseStreamResponse = + sessionServiceAsync.observeStreaming( + SessionObserveParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionObserveParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionObserveParams.XStreamResponse.TRUE) + .frameId("frameId") + .instruction("Find all clickable navigation links") + .options( + SessionObserveParams.Options.builder() + .model("openai/gpt-5-nano") + .selector("nav") + .timeout(30000.0) + .build() + ) + .build() + ) + + val onCompleteFuture = + responseStreamResponse.subscribe { response -> response.validate() }.onCompleteFuture() + onCompleteFuture.get() } @Disabled("Prism tests are disabled") @@ -254,13 +425,155 @@ internal class SessionServiceAsyncTest { val responseFuture = sessionServiceAsync.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport.builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams.BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region.US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .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 10177a3..3355c14 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 @@ -5,14 +5,14 @@ package com.browserbase.api.services.blocking import com.browserbase.api.TestServerExtension 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.SessionExecuteAgentParams +import com.browserbase.api.models.sessions.SessionEndParams +import com.browserbase.api.models.sessions.SessionExecuteParams import com.browserbase.api.models.sessions.SessionExtractParams import com.browserbase.api.models.sessions.SessionNavigateParams import com.browserbase.api.models.sessions.SessionObserveParams import com.browserbase.api.models.sessions.SessionStartParams -import kotlin.jvm.optionals.getOrNull +import java.time.OffsetDateTime import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -35,24 +35,59 @@ internal class SessionServiceTest { val response = sessionService.act( SessionActParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionActParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionActParams.XStreamResponse.TRUE) - .input("click the sign in button") + .input("Click the login button") .frameId("frameId") .options( SessionActParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) + .model("openai/gpt-5-nano") + .timeout(30000.0) + .variables( + SessionActParams.Options.Variables.builder() + .putAdditionalProperty("username", JsonValue.from("john_doe")) .build() ) - .timeout(0L) + .build() + ) + .build() + ) + + response.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun actStreaming() { + val client = + StagehandOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .browserbaseApiKey("My Browserbase API Key") + .browserbaseProjectId("My Browserbase Project ID") + .modelApiKey("My Model API Key") + .build() + val sessionService = client.sessions() + + val responseStreamResponse = + sessionService.actStreaming( + SessionActParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionActParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionActParams.XStreamResponse.TRUE) + .input("Click the login button") + .frameId("frameId") + .options( + SessionActParams.Options.builder() + .model("openai/gpt-5-nano") + .timeout(30000.0) .variables( SessionActParams.Options.Variables.builder() - .putAdditionalProperty("foo", JsonValue.from("string")) + .putAdditionalProperty("username", JsonValue.from("john_doe")) .build() ) .build() @@ -60,7 +95,9 @@ internal class SessionServiceTest { .build() ) - response.validate() + responseStreamResponse.use { + responseStreamResponse.stream().forEach { response -> response.validate() } + } } @Disabled("Prism tests are disabled") @@ -75,14 +112,23 @@ internal class SessionServiceTest { .build() val sessionService = client.sessions() - val response = sessionService.end("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + val response = + sessionService.end( + SessionEndParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionEndParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionEndParams.XStreamResponse.TRUE) + .build() + ) response.validate() } @Disabled("Prism tests are disabled") @Test - fun executeAgent() { + fun execute() { val client = StagehandOkHttpClient.builder() .baseUrl(TestServerExtension.BASE_URL) @@ -93,23 +139,27 @@ internal class SessionServiceTest { val sessionService = client.sessions() val response = - sessionService.executeAgent( - SessionExecuteAgentParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") - .xStreamResponse(SessionExecuteAgentParams.XStreamResponse.TRUE) + sessionService.execute( + SessionExecuteParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionExecuteParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionExecuteParams.XStreamResponse.TRUE) .agentConfig( - SessionExecuteAgentParams.AgentConfig.builder() + SessionExecuteParams.AgentConfig.builder() .cua(true) - .model("openai/gpt-4o") - .provider(SessionExecuteAgentParams.AgentConfig.Provider.OPENAI) + .model("openai/gpt-5-nano") .systemPrompt("systemPrompt") .build() ) .executeOptions( - SessionExecuteAgentParams.ExecuteOptions.builder() - .instruction("Find and click the first product") + SessionExecuteParams.ExecuteOptions.builder() + .instruction( + "Log in with username 'demo' and password 'test123', then navigate to settings" + ) .highlightCursor(true) - .maxSteps(10L) + .maxSteps(20.0) .build() ) .frameId("frameId") @@ -119,6 +169,51 @@ internal class SessionServiceTest { response.validate() } + @Disabled("Prism tests are disabled") + @Test + fun executeStreaming() { + val client = + StagehandOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .browserbaseApiKey("My Browserbase API Key") + .browserbaseProjectId("My Browserbase Project ID") + .modelApiKey("My Model API Key") + .build() + val sessionService = client.sessions() + + val responseStreamResponse = + sessionService.executeStreaming( + SessionExecuteParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionExecuteParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionExecuteParams.XStreamResponse.TRUE) + .agentConfig( + SessionExecuteParams.AgentConfig.builder() + .cua(true) + .model("openai/gpt-5-nano") + .systemPrompt("systemPrompt") + .build() + ) + .executeOptions( + SessionExecuteParams.ExecuteOptions.builder() + .instruction( + "Log in with username 'demo' and password 'test123', then navigate to settings" + ) + .highlightCursor(true) + .maxSteps(20.0) + .build() + ) + .frameId("frameId") + .build() + ) + + responseStreamResponse.use { + responseStreamResponse.stream().forEach { response -> response.validate() } + } + } + @Disabled("Prism tests are disabled") @Test fun extract() { @@ -134,22 +229,18 @@ internal class SessionServiceTest { val response = sessionService.extract( SessionExtractParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionExtractParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionExtractParams.XStreamResponse.TRUE) .frameId("frameId") - .instruction("extract the page title") + .instruction("Extract all product names and prices from the page") .options( SessionExtractParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .selector("selector") - .timeout(0L) + .model("openai/gpt-5-nano") + .selector("#main-content") + .timeout(30000.0) .build() ) .schema( @@ -163,6 +254,48 @@ internal class SessionServiceTest { response.validate() } + @Disabled("Prism tests are disabled") + @Test + fun extractStreaming() { + val client = + StagehandOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .browserbaseApiKey("My Browserbase API Key") + .browserbaseProjectId("My Browserbase Project ID") + .modelApiKey("My Model API Key") + .build() + val sessionService = client.sessions() + + val responseStreamResponse = + sessionService.extractStreaming( + SessionExtractParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionExtractParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionExtractParams.XStreamResponse.TRUE) + .frameId("frameId") + .instruction("Extract all product names and prices from the page") + .options( + SessionExtractParams.Options.builder() + .model("openai/gpt-5-nano") + .selector("#main-content") + .timeout(30000.0) + .build() + ) + .schema( + SessionExtractParams.Schema.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + + responseStreamResponse.use { + responseStreamResponse.stream().forEach { response -> response.validate() } + } + } + @Disabled("Prism tests are disabled") @Test fun navigate() { @@ -178,20 +311,25 @@ internal class SessionServiceTest { val response = sessionService.navigate( SessionNavigateParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionNavigateParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionNavigateParams.XStreamResponse.TRUE) .url("https://example.com") .frameId("frameId") .options( SessionNavigateParams.Options.builder() - .waitUntil(SessionNavigateParams.Options.WaitUntil.LOAD) + .referer("referer") + .timeout(30000.0) + .waitUntil(SessionNavigateParams.Options.WaitUntil.NETWORKIDLE) .build() ) + .streamResponse(true) .build() ) - val unwrappedResponse = response.getOrNull() - unwrappedResponse?.validate() + response.validate() } @Disabled("Prism tests are disabled") @@ -206,31 +344,64 @@ internal class SessionServiceTest { .build() val sessionService = client.sessions() - val actions = + val response = sessionService.observe( SessionObserveParams.builder() - .sessionId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionObserveParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionObserveParams.XStreamResponse.TRUE) .frameId("frameId") - .instruction("instruction") + .instruction("Find all clickable navigation links") .options( SessionObserveParams.Options.builder() - .model( - ModelConfig.builder() - .apiKey("apiKey") - .baseUrl("https://example.com") - .model("model") - .provider(ModelConfig.Provider.OPENAI) - .build() - ) - .selector("selector") - .timeout(0L) + .model("openai/gpt-5-nano") + .selector("nav") + .timeout(30000.0) .build() ) .build() ) - actions.forEach { it.validate() } + response.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun observeStreaming() { + val client = + StagehandOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .browserbaseApiKey("My Browserbase API Key") + .browserbaseProjectId("My Browserbase Project ID") + .modelApiKey("My Model API Key") + .build() + val sessionService = client.sessions() + + val responseStreamResponse = + sessionService.observeStreaming( + SessionObserveParams.builder() + .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .xLanguage(SessionObserveParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionObserveParams.XStreamResponse.TRUE) + .frameId("frameId") + .instruction("Find all clickable navigation links") + .options( + SessionObserveParams.Options.builder() + .model("openai/gpt-5-nano") + .selector("nav") + .timeout(30000.0) + .build() + ) + .build() + ) + + responseStreamResponse.use { + responseStreamResponse.stream().forEach { response -> response.validate() } + } } @Disabled("Prism tests are disabled") @@ -248,13 +419,155 @@ internal class SessionServiceTest { val response = sessionService.start( SessionStartParams.builder() - .browserbaseApiKey("BROWSERBASE_API_KEY") - .browserbaseProjectId("BROWSERBASE_PROJECT_ID") - .domSettleTimeout(0L) - .model("openai/gpt-4o") + .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) + .xSdkVersion("3.0.6") + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) + .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) + .modelName("gpt-4o") + .actTimeoutMs(30000.0) + .browser( + SessionStartParams.Browser.builder() + .cdpUrl("ws://localhost:9222") + .launchOptions( + SessionStartParams.Browser.LaunchOptions.builder() + .acceptDownloads(true) + .addArg("string") + .cdpUrl("cdpUrl") + .chromiumSandbox(true) + .connectTimeoutMs(0.0) + .deviceScaleFactor(0.0) + .devtools(true) + .downloadsPath("downloadsPath") + .executablePath("executablePath") + .hasTouch(true) + .headless(true) + .ignoreDefaultArgs(true) + .ignoreHttpsErrors(true) + .locale("locale") + .preserveUserDataDir(true) + .proxy( + SessionStartParams.Browser.LaunchOptions.Proxy.builder() + .server("server") + .bypass("bypass") + .password("password") + .username("username") + .build() + ) + .userDataDir("userDataDir") + .viewport( + SessionStartParams.Browser.LaunchOptions.Viewport.builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .type(SessionStartParams.Browser.Type.LOCAL) + .build() + ) + .browserbaseSessionCreateParams( + SessionStartParams.BrowserbaseSessionCreateParams.builder() + .browserSettings( + SessionStartParams.BrowserbaseSessionCreateParams.BrowserSettings + .builder() + .advancedStealth(true) + .blockAds(true) + .context( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Context + .builder() + .id("id") + .persist(true) + .build() + ) + .extensionId("extensionId") + .fingerprint( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .builder() + .addBrowser( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Browser + .CHROME + ) + .addDevice( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Device + .DESKTOP + ) + .httpVersion( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .HttpVersion + ._1 + ) + .addLocale("string") + .addOperatingSystem( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .OperatingSystem + .ANDROID + ) + .screen( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Fingerprint + .Screen + .builder() + .maxHeight(0.0) + .maxWidth(0.0) + .minHeight(0.0) + .minWidth(0.0) + .build() + ) + .build() + ) + .logSession(true) + .recordSession(true) + .solveCaptchas(true) + .viewport( + SessionStartParams.BrowserbaseSessionCreateParams + .BrowserSettings + .Viewport + .builder() + .height(0.0) + .width(0.0) + .build() + ) + .build() + ) + .extensionId("extensionId") + .keepAlive(true) + .projectId("projectId") + .proxies(true) + .region( + SessionStartParams.BrowserbaseSessionCreateParams.Region.US_WEST_2 + ) + .timeout(0.0) + .userMetadata( + SessionStartParams.BrowserbaseSessionCreateParams.UserMetadata + .builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + .browserbaseSessionId("browserbaseSessionID") + .debugDom(true) + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") .verbose(1L) + .waitForCaptchaSolves(true) .build() ) diff --git a/stagehand-java-proguard-test/src/test/kotlin/com/browserbase/api/proguard/ProGuardCompatibilityTest.kt b/stagehand-java-proguard-test/src/test/kotlin/com/browserbase/api/proguard/ProGuardCompatibilityTest.kt index 3960aa2..0d52560 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 @@ -5,7 +5,7 @@ package com.browserbase.api.proguard import com.browserbase.api.client.okhttp.StagehandOkHttpClient import com.browserbase.api.core.jsonMapper import com.browserbase.api.models.sessions.Action -import com.browserbase.api.models.sessions.SessionExtractResponse +import com.browserbase.api.models.sessions.ModelConfig import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import kotlin.reflect.full.memberFunctions import kotlin.reflect.jvm.javaMethod @@ -61,11 +61,10 @@ internal class ProGuardCompatibilityTest { val jsonMapper = jsonMapper() val action = Action.builder() - .addArgument("string") - .description("description") - .method("method") - .selector("selector") - .backendNodeId(0L) + .description("Click the submit button") + .selector("[data-testid='submit-button']") + .addArgument("Hello World") + .method("click") .build() val roundtrippedAction = @@ -75,19 +74,16 @@ internal class ProGuardCompatibilityTest { } @Test - fun sessionExtractResponseRoundtrip() { + fun modelConfigRoundtrip() { val jsonMapper = jsonMapper() - val sessionExtractResponse = - SessionExtractResponse.ofExtraction( - SessionExtractResponse.Extraction.builder().extraction("extraction").build() - ) + val modelConfig = ModelConfig.ofName("openai/gpt-5-nano") - val roundtrippedSessionExtractResponse = + val roundtrippedModelConfig = jsonMapper.readValue( - jsonMapper.writeValueAsString(sessionExtractResponse), - jacksonTypeRef(), + jsonMapper.writeValueAsString(modelConfig), + jacksonTypeRef(), ) - assertThat(roundtrippedSessionExtractResponse).isEqualTo(sessionExtractResponse) + assertThat(roundtrippedModelConfig).isEqualTo(modelConfig) } }