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..5a89fb7 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-ed52466945f2f8dfd3814a29e948d7bf30af7b76a7a7689079c03b8baf64e26f.yml +openapi_spec_hash: 5d57aaf2362b0d882372dbf76477ba23 +config_hash: 989ddfee371586e9156b4d484ec0a6cc diff --git a/CHANGELOG.md b/CHANGELOG.md index 04011d6..81e4589 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## 0.3.0 (2025-12-23) + +Full Changelog: [v0.2.0...v0.3.0](https://github.com/browserbase/stagehand-java/compare/v0.2.0...v0.3.0) + +### Features + +* [STG-1053] [server] Use fastify-zod-openapi + zod v4 for openapi generation ([9de17a2](https://github.com/browserbase/stagehand-java/commit/9de17a2552f759f6e8f37de4c3b9bb5d403edcc4)) +* **api:** manual updates ([d9e462c](https://github.com/browserbase/stagehand-java/commit/d9e462c24ed6d3e165a5873e7f70f011d768500a)) +* **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)) + + +### Documentation + +* add more examples ([54eba34](https://github.com/browserbase/stagehand-java/commit/54eba3455dbfc5dd61c1c3b88aedd34c56bce37a)) + ## 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..ab416ca 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); @@ -190,6 +190,98 @@ CompletableFuture response = client.sessions().act(params); The asynchronous client supports the same options as the synchronous one, except most methods return `CompletableFuture`s. +## Streaming + +The SDK defines methods that return response "chunk" streams, where each chunk can be individually processed as soon as it arrives instead of waiting on the full response. Streaming methods generally correspond to [SSE](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) or [JSONL](https://jsonlines.org) responses. + +Some of these methods may have streaming and non-streaming variants, but a streaming method will always have a `Streaming` suffix in its name, even if it doesn't have a non-streaming variant. + +These streaming methods return [`StreamResponse`](stagehand-java-core/src/main/kotlin/com/browserbase/api/core/http/StreamResponse.kt) for synchronous clients: + +```java +import com.browserbase.api.core.http.StreamResponse; +import com.browserbase.api.models.sessions.StreamEvent; + +try (StreamResponse streamResponse = client.sessions().actStreaming(params)) { + streamResponse.stream().forEach(chunk -> { + System.out.println(chunk); + }); + System.out.println("No more chunks!"); +} +``` + +Or [`AsyncStreamResponse`](stagehand-java-core/src/main/kotlin/com/browserbase/api/core/http/AsyncStreamResponse.kt) for asynchronous clients: + +```java +import com.browserbase.api.core.http.AsyncStreamResponse; +import com.browserbase.api.models.sessions.StreamEvent; +import java.util.Optional; + +client.async().sessions().actStreaming(params).subscribe(chunk -> { + System.out.println(chunk); +}); + +// If you need to handle errors or completion of the stream +client.async().sessions().actStreaming(params).subscribe(new AsyncStreamResponse.Handler<>() { + @Override + public void onNext(StreamEvent chunk) { + System.out.println(chunk); + } + + @Override + public void onComplete(Optional error) { + if (error.isPresent()) { + System.out.println("Something went wrong!"); + throw new RuntimeException(error.get()); + } else { + System.out.println("No more chunks!"); + } + } +}); + +// Or use futures +client.async().sessions().actStreaming(params) + .subscribe(chunk -> { + System.out.println(chunk); + }) + .onCompleteFuture(); + .whenComplete((unused, error) -> { + if (error != null) { + System.out.println("Something went wrong!"); + throw new RuntimeException(error); + } else { + System.out.println("No more chunks!"); + } + }); +``` + +Async streaming uses a dedicated per-client cached thread pool [`Executor`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html) to stream without blocking the current thread. This default is suitable for most purposes. + +To use a different `Executor`, configure the subscription using the `executor` parameter: + +```java +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +Executor executor = Executors.newFixedThreadPool(4); +client.async().sessions().actStreaming(params).subscribe( + chunk -> System.out.println(chunk), executor +); +``` + +Or configure the client globally using the `streamHandlerExecutor` method: + +```java +import com.browserbase.api.client.StagehandClient; +import com.browserbase.api.client.okhttp.StagehandOkHttpClient; +import java.util.concurrent.Executors; + +StagehandClient client = StagehandOkHttpClient.builder() + .fromEnv() + .streamHandlerExecutor(Executors.newFixedThreadPool(4)) + .build(); +``` + ## Raw responses The SDK defines methods that deserialize responses into instances of Java classes. However, these methods don't provide access to the response headers, status code, or the raw response body. @@ -203,8 +295,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 +328,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 +598,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..2ce760b 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,32 @@ 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 backendNodeId: 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("arguments") + @ExcludeMissing + arguments: JsonField> = JsonMissing.of(), @JsonProperty("backendNodeId") @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") + backendNodeId: JsonField = JsonMissing.of(), + @JsonProperty("method") @ExcludeMissing method: JsonField = JsonMissing.of(), + ) : this(description, selector, arguments, backendNodeId, method, mutableMapOf()) /** * Human-readable description of the action @@ -62,35 +55,36 @@ private constructor( fun description(): String = description.getRequired("description") /** - * Method to execute (e.g., "click", "fill") + * 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). */ - fun method(): String = method.getRequired("method") + fun selector(): String = selector.getRequired("selector") /** - * CSS or XPath selector for the element + * Arguments to pass to 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). + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). */ - fun selector(): String = selector.getRequired("selector") + fun arguments(): Optional> = arguments.getOptional("arguments") /** - * CDP backend node ID + * Backend node ID for the element * * @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 backendNodeId(): Optional = backendNodeId.getOptional("backendNodeId") /** - * 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,18 +94,18 @@ 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]. @@ -120,7 +114,14 @@ private constructor( */ @JsonProperty("backendNodeId") @ExcludeMissing - fun _backendNodeId(): JsonField = backendNodeId + fun _backendNodeId(): JsonField = backendNodeId + + /** + * Returns the raw JSON value of [method]. + * + * Unlike [method], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("method") @ExcludeMissing fun _method(): JsonField = method @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -141,9 +142,7 @@ private constructor( * * The following fields are required: * ```java - * .arguments() * .description() - * .method() * .selector() * ``` */ @@ -153,24 +152,47 @@ 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 backendNodeId: JsonField = JsonMissing.of() + 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 + arguments = action.arguments.map { it.toMutableList() } backendNodeId = action.backendNodeId + 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 +218,21 @@ private constructor( } } - /** Human-readable description of the action */ - fun description(description: String) = description(JsonField.of(description)) + /** Backend node ID for the element */ + fun backendNodeId(backendNodeId: Double) = backendNodeId(JsonField.of(backendNodeId)) /** - * Sets [Builder.description] to an arbitrary JSON value. + * Sets [Builder.backendNodeId] to an arbitrary JSON value. * - * You should usually call [Builder.description] with a well-typed [String] value instead. + * You should usually call [Builder.backendNodeId] with a well-typed [Double] 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 } + fun backendNodeId(backendNodeId: JsonField) = apply { + this.backendNodeId = backendNodeId + } - /** Method to execute (e.g., "click", "fill") */ + /** The method to execute (click, fill, etc.) */ fun method(method: String) = method(JsonField.of(method)) /** @@ -219,31 +243,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 +269,7 @@ private constructor( * * The following fields are required: * ```java - * .arguments() * .description() - * .method() * .selector() * ``` * @@ -280,11 +277,11 @@ private constructor( */ fun build(): Action = Action( - checkRequired("arguments", arguments).map { it.toImmutable() }, checkRequired("description", description), - checkRequired("method", method), checkRequired("selector", selector), + (arguments ?: JsonMissing.of()).map { it.toImmutable() }, backendNodeId, + method, additionalProperties.toMutableMap(), ) } @@ -296,11 +293,11 @@ private constructor( return@apply } - arguments() description() - method() selector() + arguments() backendNodeId() + method() validated = true } @@ -319,11 +316,11 @@ 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 (backendNodeId.asKnown().isPresent) 1 else 0) + + (if (method.asKnown().isPresent) 1 else 0) override fun equals(other: Any?): Boolean { if (this === other) { @@ -331,20 +328,20 @@ private constructor( } return other is Action && - arguments == other.arguments && description == other.description && - method == other.method && selector == other.selector && + arguments == other.arguments && backendNodeId == other.backendNodeId && + 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, backendNodeId, 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, backendNodeId=$backendNodeId, 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..bfbf0aa 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,340 +2,449 @@ 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 +/** + * 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 + fun asName(): String = name.getOrThrow("name") - /** - * 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 + fun asModelConfigObject(): ModelConfigObject = modelConfigObject.getOrThrow("modelConfigObject") - /** - * 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 + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + name != null -> visitor.visitName(name) + modelConfigObject != null -> visitor.visitModelConfigObject(modelConfigObject) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): ModelConfig = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitName(name: String) {} + + override fun visitModelConfigObject(modelConfigObject: ModelConfigObject) { + modelConfigObject.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + false + } /** - * Returns the raw JSON value of [provider]. + * Returns a score indicating how many valid values are contained in this object recursively. * - * Unlike [provider], this method doesn't throw if the JSON field has an unexpected type. + * Used for best match union deserialization. */ - @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonField = provider - - @JsonAnySetter - private fun putAdditionalProperty(key: String, value: JsonValue) { - additionalProperties.put(key, value) - } + @JvmSynthetic + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitName(name: String) = 1 - @JsonAnyGetter - @ExcludeMissing - fun _additionalProperties(): Map = - Collections.unmodifiableMap(additionalProperties) + override fun visitModelConfigObject(modelConfigObject: ModelConfigObject) = + modelConfigObject.validity() - fun toBuilder() = Builder().from(this) + override fun unknown(json: JsonValue?) = 0 + } + ) - companion object { + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } - /** Returns a mutable builder for constructing an instance of [ModelConfig]. */ - @JvmStatic fun builder() = Builder() + return other is ModelConfig && + name == other.name && + modelConfigObject == other.modelConfigObject } - /** 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() + override fun hashCode(): Int = Objects.hash(name, modelConfigObject) - @JvmSynthetic - internal fun from(modelConfig: ModelConfig) = apply { - apiKey = modelConfig.apiKey - baseUrl = modelConfig.baseUrl - model = modelConfig.model - provider = modelConfig.provider - additionalProperties = modelConfig.additionalProperties.toMutableMap() + 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") } - /** API key for the model provider */ - fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) + companion object { /** - * 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. + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') */ - fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } + @JvmStatic fun ofName(name: String) = ModelConfig(name = name) - /** 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 } + @JvmStatic + fun ofModelConfigObject(modelConfigObject: ModelConfigObject) = + ModelConfig(modelConfigObject = modelConfigObject) + } - /** Model name */ - fun model(model: String) = model(JsonField.of(model)) + /** + * An interface that defines how to map each variant of [ModelConfig] to a value of type [T]. + */ + interface Visitor { /** - * 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. + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') */ - fun model(model: JsonField) = apply { this.model = model } + fun visitName(name: String): T - fun provider(provider: Provider) = provider(JsonField.of(provider)) + fun visitModelConfigObject(modelConfigObject: ModelConfigObject): T /** - * Sets [Builder.provider] to an arbitrary JSON value. + * Maps an unknown variant of [ModelConfig] to a value of type [T]. * - * 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. + * 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 provider(provider: JsonField) = apply { this.provider = provider } - - fun additionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.clear() - putAllAdditionalProperties(additionalProperties) + fun unknown(json: JsonValue?): T { + throw StagehandInvalidDataException("Unknown ModelConfig: $json") } + } - fun putAdditionalProperty(key: String, value: JsonValue) = apply { - additionalProperties.put(key, value) + 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() + } } + } - fun putAllAdditionalProperties(additionalProperties: Map) = apply { - this.additionalProperties.putAll(additionalProperties) + 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") + } } + } - fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } - - fun removeAllAdditionalProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalProperty) - } + class ModelConfigObject + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val modelName: JsonField, + private val apiKey: JsonField, + private val baseUrl: JsonField, + private val provider: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("modelName") + @ExcludeMissing + modelName: JsonField = JsonMissing.of(), + @JsonProperty("apiKey") @ExcludeMissing apiKey: JsonField = JsonMissing.of(), + @JsonProperty("baseURL") @ExcludeMissing baseUrl: JsonField = JsonMissing.of(), + @JsonProperty("provider") + @ExcludeMissing + provider: JsonField = JsonMissing.of(), + ) : this(modelName, apiKey, baseUrl, provider, mutableMapOf()) /** - * Returns an immutable instance of [ModelConfig]. + * Model name string without prefix (e.g., 'gpt-5-nano', 'claude-4.5-opus') * - * Further updates to this [Builder] will not mutate the returned instance. + * @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 build(): ModelConfig = - ModelConfig(apiKey, baseUrl, model, provider, additionalProperties.toMutableMap()) - } + fun modelName(): String = modelName.getRequired("modelName") - private var validated: Boolean = false + /** + * API key for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun apiKey(): Optional = apiKey.getOptional("apiKey") - fun validate(): ModelConfig = apply { - if (validated) { - return@apply - } + /** + * Base URL for the model provider + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun baseUrl(): Optional = baseUrl.getOptional("baseURL") - apiKey() - baseUrl() - model() - provider().ifPresent { it.validate() } - validated = true - } + /** + * AI provider for the model (or provide a baseURL endpoint instead) + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun provider(): Optional = provider.getOptional("provider") - fun isValid(): Boolean = - try { - validate() - true - } catch (e: StagehandInvalidDataException) { - false - } + /** + * 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 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 (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) + /** + * Returns the raw JSON value of [apiKey]. + * + * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey - class Provider @JsonCreator private constructor(private val value: JsonField) : Enum { + /** + * 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 this class instance's raw value. + * Returns the raw JSON value of [provider]. * - * 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. + * Unlike [provider], this method doesn't throw if the JSON field has an unexpected type. */ - @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonField = provider - companion object { + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } - @JvmField val OPENAI = of("openai") + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) - @JvmField val ANTHROPIC = of("anthropic") + fun toBuilder() = Builder().from(this) - @JvmField val GOOGLE = of("google") + companion object { - @JvmStatic fun of(value: String) = Provider(JsonField.of(value)) + /** + * Returns a mutable builder for constructing an instance of [ModelConfigObject]. + * + * The following fields are required: + * ```java + * .modelName() + * ``` + */ + @JvmStatic fun builder() = Builder() } - /** An enum containing [Provider]'s known values. */ - enum class Known { - OPENAI, - ANTHROPIC, - GOOGLE, - } + /** 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 provider: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(modelConfigObject: ModelConfigObject) = apply { + modelName = modelConfigObject.modelName + apiKey = modelConfigObject.apiKey + baseUrl = modelConfigObject.baseUrl + provider = modelConfigObject.provider + additionalProperties = modelConfigObject.additionalProperties.toMutableMap() + } - /** - * 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, - } + /** 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 } + + /** AI provider for the model (or provide a baseURL endpoint instead) */ + fun provider(provider: Provider) = provider(JsonField.of(provider)) + + /** + * Sets [Builder.provider] to an arbitrary JSON value. + * + * You should usually call [Builder.provider] with a well-typed [Provider] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun provider(provider: JsonField) = apply { this.provider = provider } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } - /** - * 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 + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) } - /** - * Returns an enum member corresponding to this class instance's value. - * - * Use the [value] method instead if you're uncertain the value is always known and don't - * want to throw for the unknown case. - * - * @throws StagehandInvalidDataException if this class instance's value is a not a known - * member. - */ - fun known(): Known = - when (this) { - OPENAI -> Known.OPENAI - ANTHROPIC -> Known.ANTHROPIC - GOOGLE -> Known.GOOGLE - else -> throw StagehandInvalidDataException("Unknown Provider: $value") + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) } - /** - * 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") + 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, + provider, + additionalProperties.toMutableMap(), + ) + } + private var validated: Boolean = false - fun validate(): Provider = apply { + fun validate(): ModelConfigObject = apply { if (validated) { return@apply } - known() + modelName() + apiKey() + baseUrl() + provider().ifPresent { it.validate() } validated = true } @@ -353,40 +462,176 @@ 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) + + (provider.asKnown().getOrNull()?.validity() ?: 0) + + /** AI provider for the model (or provide a baseURL endpoint instead) */ + class Provider @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val OPENAI = of("openai") + + @JvmField val ANTHROPIC = of("anthropic") + + @JvmField val GOOGLE = of("google") + + @JvmField val MICROSOFT = of("microsoft") + + @JvmStatic fun of(value: String) = Provider(JsonField.of(value)) + } + + /** An enum containing [Provider]'s known values. */ + enum class Known { + OPENAI, + ANTHROPIC, + GOOGLE, + MICROSOFT, + } + + /** + * An enum containing [Provider]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Provider] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + OPENAI, + ANTHROPIC, + GOOGLE, + MICROSOFT, + /** + * An enum member indicating that [Provider] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + OPENAI -> Value.OPENAI + ANTHROPIC -> Value.ANTHROPIC + GOOGLE -> Value.GOOGLE + MICROSOFT -> Value.MICROSOFT + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws StagehandInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + OPENAI -> Known.OPENAI + ANTHROPIC -> Known.ANTHROPIC + GOOGLE -> Known.GOOGLE + MICROSOFT -> Known.MICROSOFT + 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 } - return other is Provider && value == other.value + return other is ModelConfigObject && + modelName == other.modelName && + apiKey == other.apiKey && + baseUrl == other.baseUrl && + provider == other.provider && + additionalProperties == other.additionalProperties } - override fun hashCode() = value.hashCode() - - override fun toString() = value.toString() - } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true + private val hashCode: Int by lazy { + Objects.hash(modelName, apiKey, baseUrl, provider, 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, provider=$provider, 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..62bec51 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,867 @@ 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) + + /** Action object returned by observe and used by act */ + class Action + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val description: JsonField, + private val selector: JsonField, + private val arguments: JsonField>, + private val backendNodeId: JsonField, + private val method: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("description") + @ExcludeMissing + description: JsonField = JsonMissing.of(), + @JsonProperty("selector") + @ExcludeMissing + selector: JsonField = JsonMissing.of(), + @JsonProperty("arguments") + @ExcludeMissing + arguments: JsonField> = JsonMissing.of(), + @JsonProperty("backendNodeId") + @ExcludeMissing + backendNodeId: JsonField = JsonMissing.of(), + @JsonProperty("method") + @ExcludeMissing + method: JsonField = JsonMissing.of(), + ) : this(description, selector, arguments, backendNodeId, method, mutableMapOf()) + + /** + * Human-readable description of the action + * + * @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 description(): String = description.getRequired("description") + + /** + * 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). + */ + fun selector(): String = selector.getRequired("selector") + + /** + * 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 arguments(): Optional> = arguments.getOptional("arguments") + + /** + * Backend node ID for the element + * + * @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") + + /** + * The method to execute (click, fill, etc.) + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun method(): Optional = method.getOptional("method") + + /** + * Returns the raw JSON value of [description]. + * + * Unlike [description], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("description") + @ExcludeMissing + fun _description(): JsonField = description + + /** + * Returns the raw JSON value of [selector]. + * + * Unlike [selector], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("selector") + @ExcludeMissing + fun _selector(): JsonField = selector + + /** + * Returns the raw JSON value of [arguments]. + * + * Unlike [arguments], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("arguments") + @ExcludeMissing + fun _arguments(): JsonField> = arguments + + /** + * Returns the raw JSON value of [backendNodeId]. + * + * Unlike [backendNodeId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("backendNodeId") + @ExcludeMissing + fun _backendNodeId(): JsonField = backendNodeId + + /** + * Returns the raw JSON value of [method]. + * + * Unlike [method], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("method") @ExcludeMissing fun _method(): JsonField = method + + @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 + * .description() + * .selector() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Action]. */ + class Builder internal constructor() { + + private var description: JsonField? = null + private var selector: JsonField? = null + private var arguments: JsonField>? = null + private var backendNodeId: JsonField = JsonMissing.of() + private var method: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(action: Action) = apply { + description = action.description + selector = action.selector + arguments = action.arguments.map { it.toMutableList() } + backendNodeId = action.backendNodeId + method = action.method + additionalProperties = action.additionalProperties.toMutableMap() + } + + /** 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)) + + /** + * Sets [Builder.arguments] to an arbitrary JSON value. + * + * You should usually call [Builder.arguments] with a well-typed `List` + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun arguments(arguments: JsonField>) = apply { + this.arguments = arguments.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [arguments]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addArgument(argument: String) = apply { + arguments = + (arguments ?: JsonField.of(mutableListOf())).also { + checkKnown("arguments", it).add(argument) + } + } + + /** Backend node ID for the element */ + fun backendNodeId(backendNodeId: Double) = + backendNodeId(JsonField.of(backendNodeId)) + + /** + * Sets [Builder.backendNodeId] to an arbitrary JSON value. + * + * You should usually call [Builder.backendNodeId] with a well-typed [Double] + * 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 + } + + /** The method to execute (click, fill, etc.) */ + fun method(method: String) = method(JsonField.of(method)) + + /** + * Sets [Builder.method] to an arbitrary JSON value. + * + * You should usually call [Builder.method] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun method(method: JsonField) = apply { this.method = method } + + 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 + * .description() + * .selector() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Action = + Action( + checkRequired("description", description), + checkRequired("selector", selector), + (arguments ?: JsonMissing.of()).map { it.toImmutable() }, + backendNodeId, + method, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Action = apply { + if (validated) { + return@apply + } + + description() + selector() + arguments() + backendNodeId() + method() + 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 (description.asKnown().isPresent) 1 else 0) + + (if (selector.asKnown().isPresent) 1 else 0) + + (arguments.asKnown().getOrNull()?.size ?: 0) + + (if (backendNodeId.asKnown().isPresent) 1 else 0) + + (if (method.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Action && + description == other.description && + selector == other.selector && + arguments == other.arguments && + backendNodeId == other.backendNodeId && + method == other.method && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + description, + selector, + arguments, + backendNodeId, + method, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Action{description=$description, selector=$selector, arguments=$arguments, backendNodeId=$backendNodeId, method=$method, additionalProperties=$additionalProperties}" + } + + 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 +1057,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 81% 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..8cb0da8 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()) - /** Alias for calling [Builder.sessionId] with `sessionId.orElse(null)`. */ - fun sessionId(sessionId: Optional) = sessionId(sessionId.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 } @@ -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,7 +634,7 @@ private constructor( @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val cua: JsonField, - private val model: JsonField, + private val model: JsonField, private val provider: JsonField, private val systemPrompt: JsonField, private val additionalProperties: MutableMap, @@ -600,7 +643,7 @@ private constructor( @JsonCreator private constructor( @JsonProperty("cua") @ExcludeMissing cua: JsonField = JsonMissing.of(), - @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), @JsonProperty("provider") @ExcludeMissing provider: JsonField = JsonMissing.of(), @@ -618,18 +661,25 @@ 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") + fun model(): Optional = model.getOptional("model") /** + * AI provider for the agent (legacy, use model: openai/gpt-5-nano instead) + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ fun provider(): Optional = provider.getOptional("provider") /** + * 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,7 +697,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 + @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model /** * Returns the raw JSON value of [provider]. @@ -688,7 +738,7 @@ private constructor( class Builder internal constructor() { private var cua: JsonField = JsonMissing.of() - private var model: JsonField = JsonMissing.of() + private var model: JsonField = JsonMissing.of() private var provider: JsonField = JsonMissing.of() private var systemPrompt: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -714,23 +764,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 } + fun model(model: JsonField) = apply { this.model = model } - /** Alias for calling [model] with `Model.ofString(string)`. */ - fun model(string: String) = model(Model.ofString(string)) + /** Alias for calling [model] with `ModelConfig.ofName(name)`. */ + fun model(name: String) = model(ModelConfig.ofName(name)) - /** Alias for calling [model] with `Model.ofConfig(config)`. */ - fun model(config: ModelConfig) = model(Model.ofConfig(config)) + /** + * Alias for calling [model] with `ModelConfig.ofModelConfigObject(modelConfigObject)`. + */ + fun model(modelConfigObject: ModelConfig.ModelConfigObject) = + model(ModelConfig.ofModelConfigObject(modelConfigObject)) + /** AI provider for the agent (legacy, use model: openai/gpt-5-nano instead) */ fun provider(provider: Provider) = provider(JsonField.of(provider)) /** @@ -742,6 +800,7 @@ private constructor( */ fun provider(provider: JsonField) = apply { this.provider = provider } + /** Custom system prompt for the agent */ fun systemPrompt(systemPrompt: String) = systemPrompt(JsonField.of(systemPrompt)) /** @@ -818,177 +877,7 @@ private constructor( (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") - } - } - } - } - + /** AI provider for the agent (legacy, use model: openai/gpt-5-nano instead) */ class Provider @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -1010,6 +899,8 @@ private constructor( @JvmField val GOOGLE = of("google") + @JvmField val MICROSOFT = of("microsoft") + @JvmStatic fun of(value: String) = Provider(JsonField.of(value)) } @@ -1018,6 +909,7 @@ private constructor( OPENAI, ANTHROPIC, GOOGLE, + MICROSOFT, } /** @@ -1033,6 +925,7 @@ private constructor( OPENAI, ANTHROPIC, GOOGLE, + MICROSOFT, /** * An enum member indicating that [Provider] was instantiated with an unknown value. */ @@ -1051,6 +944,7 @@ private constructor( OPENAI -> Value.OPENAI ANTHROPIC -> Value.ANTHROPIC GOOGLE -> Value.GOOGLE + MICROSOFT -> Value.MICROSOFT else -> Value._UNKNOWN } @@ -1068,6 +962,7 @@ private constructor( OPENAI -> Known.OPENAI ANTHROPIC -> Known.ANTHROPIC GOOGLE -> Known.GOOGLE + MICROSOFT -> Known.MICROSOFT else -> throw StagehandInvalidDataException("Unknown Provider: $value") } @@ -1153,7 +1048,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 +1060,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 +1072,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 +1085,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 +1111,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 +1143,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 +1154,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 +1168,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 +1184,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 +1290,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 +1563,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 +1575,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..9c1c551 --- /dev/null +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveResponse.kt @@ -0,0 +1,775 @@ +// 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 [Result] to [Builder.result]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addResult(result: Result) = 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) + + /** Action object returned by observe and used by act */ + class Result + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val description: JsonField, + private val selector: JsonField, + private val arguments: JsonField>, + private val backendNodeId: JsonField, + private val method: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("description") + @ExcludeMissing + description: JsonField = JsonMissing.of(), + @JsonProperty("selector") + @ExcludeMissing + selector: JsonField = JsonMissing.of(), + @JsonProperty("arguments") + @ExcludeMissing + arguments: JsonField> = JsonMissing.of(), + @JsonProperty("backendNodeId") + @ExcludeMissing + backendNodeId: JsonField = JsonMissing.of(), + @JsonProperty("method") @ExcludeMissing method: JsonField = JsonMissing.of(), + ) : this(description, selector, arguments, backendNodeId, method, mutableMapOf()) + + /** + * Human-readable description of the action + * + * @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 description(): String = description.getRequired("description") + + /** + * 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). + */ + fun selector(): String = selector.getRequired("selector") + + /** + * 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 arguments(): Optional> = arguments.getOptional("arguments") + + /** + * Backend node ID for the element + * + * @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") + + /** + * The method to execute (click, fill, etc.) + * + * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun method(): Optional = method.getOptional("method") + + /** + * Returns the raw JSON value of [description]. + * + * Unlike [description], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("description") + @ExcludeMissing + fun _description(): JsonField = description + + /** + * Returns the raw JSON value of [selector]. + * + * Unlike [selector], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("selector") @ExcludeMissing fun _selector(): JsonField = selector + + /** + * Returns the raw JSON value of [arguments]. + * + * Unlike [arguments], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("arguments") + @ExcludeMissing + fun _arguments(): JsonField> = arguments + + /** + * Returns the raw JSON value of [backendNodeId]. + * + * Unlike [backendNodeId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("backendNodeId") + @ExcludeMissing + fun _backendNodeId(): JsonField = backendNodeId + + /** + * Returns the raw JSON value of [method]. + * + * Unlike [method], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("method") @ExcludeMissing fun _method(): JsonField = method + + @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 + * .description() + * .selector() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Result]. */ + class Builder internal constructor() { + + private var description: JsonField? = null + private var selector: JsonField? = null + private var arguments: JsonField>? = null + private var backendNodeId: JsonField = JsonMissing.of() + private var method: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(result: Result) = apply { + description = result.description + selector = result.selector + arguments = result.arguments.map { it.toMutableList() } + backendNodeId = result.backendNodeId + method = result.method + additionalProperties = result.additionalProperties.toMutableMap() + } + + /** 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)) + + /** + * Sets [Builder.arguments] to an arbitrary JSON value. + * + * You should usually call [Builder.arguments] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun arguments(arguments: JsonField>) = apply { + this.arguments = arguments.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [arguments]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addArgument(argument: String) = apply { + arguments = + (arguments ?: JsonField.of(mutableListOf())).also { + checkKnown("arguments", it).add(argument) + } + } + + /** Backend node ID for the element */ + fun backendNodeId(backendNodeId: Double) = + backendNodeId(JsonField.of(backendNodeId)) + + /** + * Sets [Builder.backendNodeId] to an arbitrary JSON value. + * + * You should usually call [Builder.backendNodeId] with a well-typed [Double] 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 + } + + /** The method to execute (click, fill, etc.) */ + fun method(method: String) = method(JsonField.of(method)) + + /** + * Sets [Builder.method] to an arbitrary JSON value. + * + * You should usually call [Builder.method] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun method(method: JsonField) = apply { this.method = method } + + 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 + * .description() + * .selector() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Result = + Result( + checkRequired("description", description), + checkRequired("selector", selector), + (arguments ?: JsonMissing.of()).map { it.toImmutable() }, + backendNodeId, + method, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Result = apply { + if (validated) { + return@apply + } + + description() + selector() + arguments() + backendNodeId() + method() + 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 (description.asKnown().isPresent) 1 else 0) + + (if (selector.asKnown().isPresent) 1 else 0) + + (arguments.asKnown().getOrNull()?.size ?: 0) + + (if (backendNodeId.asKnown().isPresent) 1 else 0) + + (if (method.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Result && + description == other.description && + selector == other.selector && + arguments == other.arguments && + backendNodeId == other.backendNodeId && + method == other.method && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + description, + selector, + arguments, + backendNodeId, + method, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Result{description=$description, selector=$selector, arguments=$arguments, backendNodeId=$backendNodeId, method=$method, 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) { + 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..3a7009b 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,117 @@ 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 (deprecated, v2 only) * - * @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() /** - * 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 +123,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,43 +131,73 @@ 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). */ - fun verbose(): Optional = body.verbose() + fun verbose(): Optional = body.verbose() /** - * Returns the raw JSON value of [browserbaseApiKey]. + * Wait for captcha solves (deprecated, v2 only) * - * Unlike [browserbaseApiKey], 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). + */ + fun waitForCaptchaSolves(): Optional = body.waitForCaptchaSolves() + + /** + * Returns the raw JSON value of [modelName]. + * + * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected type. */ - fun _browserbaseApiKey(): JsonField = body._browserbaseApiKey() + fun _modelName(): JsonField = body._modelName() /** - * Returns the raw JSON value of [browserbaseProjectId]. + * Returns the raw JSON value of [actTimeoutMs]. * - * Unlike [browserbaseProjectId], 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. + */ + 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 _browserbaseSessionCreateParams(): JsonField = + body._browserbaseSessionCreateParams() + + /** + * Returns the raw JSON value of [browserbaseSessionId]. + * + * 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 [domSettleTimeoutMs]. * - * Unlike [domSettleTimeout], this method doesn't throw if the JSON field has an unexpected + * 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]. @@ -136,7 +218,15 @@ private constructor( * * Unlike [verbose], this method doesn't throw if the JSON field has an unexpected type. */ - fun _verbose(): JsonField = body._verbose() + 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() @@ -155,8 +245,7 @@ private constructor( * * The following fields are required: * ```java - * .browserbaseApiKey() - * .browserbaseProjectId() + * .modelName() * ``` */ @JvmStatic fun builder() = Builder() @@ -165,89 +254,161 @@ 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 (deprecated, v2 only) */ + 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) } /** 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 +422,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,16 +436,32 @@ private constructor( body.systemPrompt(systemPrompt) } - /** Logging verbosity level */ - fun verbose(verbose: Long) = apply { body.verbose(verbose) } + /** Logging verbosity level (0=quiet, 1=normal, 2=debug) */ + fun verbose(verbose: Verbose) = apply { body.verbose(verbose) } /** * Sets [Builder.verbose] to an arbitrary JSON value. * - * You should usually call [Builder.verbose] with a well-typed [Long] value instead. This + * You should usually call [Builder.verbose] with a well-typed [Verbose] value instead. This * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun verbose(verbose: JsonField) = apply { body.verbose(verbose) } + fun verbose(verbose: JsonField) = apply { body.verbose(verbose) } + + /** Wait for captcha solves (deprecated, v2 only) */ + 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 +587,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,85 +606,135 @@ 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 domSettleTimeoutMs: JsonField, + private val experimental: JsonField, private val selfHeal: JsonField, private val systemPrompt: JsonField, - private val verbose: 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 - browserbaseApiKey: JsonField = JsonMissing.of(), - @JsonProperty("BROWSERBASE_PROJECT_ID") + actTimeoutMs: JsonField = JsonMissing.of(), + @JsonProperty("browser") @ExcludeMissing browser: JsonField = JsonMissing.of(), + @JsonProperty("browserbaseSessionCreateParams") @ExcludeMissing - browserbaseProjectId: JsonField = JsonMissing.of(), - @JsonProperty("domSettleTimeout") + browserbaseSessionCreateParams: JsonField = + JsonMissing.of(), + @JsonProperty("browserbaseSessionID") @ExcludeMissing - domSettleTimeout: JsonField = JsonMissing.of(), - @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + browserbaseSessionId: JsonField = JsonMissing.of(), + @JsonProperty("domSettleTimeoutMs") + @ExcludeMissing + domSettleTimeoutMs: JsonField = JsonMissing.of(), + @JsonProperty("experimental") + @ExcludeMissing + experimental: JsonField = JsonMissing.of(), @JsonProperty("selfHeal") @ExcludeMissing selfHeal: JsonField = JsonMissing.of(), @JsonProperty("systemPrompt") @ExcludeMissing systemPrompt: JsonField = JsonMissing.of(), - @JsonProperty("verbose") @ExcludeMissing verbose: 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, + 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 (deprecated, v2 only) * - * @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") /** - * 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 +745,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,49 +753,86 @@ 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). */ - fun verbose(): Optional = verbose.getOptional("verbose") + fun verbose(): Optional = verbose.getOptional("verbose") + + /** + * Wait for captcha solves (deprecated, v2 only) + * + * @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 [browserbaseApiKey]. + * 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 [domSettleTimeoutMs]. * - * Unlike [domSettleTimeout], this method doesn't throw if the JSON field has an unexpected - * type. + * Unlike [domSettleTimeoutMs], this method doesn't throw if the JSON field has an + * unexpected type. */ - @JsonProperty("domSettleTimeout") + @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]. @@ -589,7 +856,17 @@ private constructor( * * Unlike [verbose], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("verbose") @ExcludeMissing fun _verbose(): JsonField = verbose + @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) { @@ -610,8 +887,7 @@ private constructor( * * The following fields are required: * ```java - * .browserbaseApiKey() - * .browserbaseProjectId() + * .modelName() * ``` */ @JvmStatic fun builder() = Builder() @@ -620,83 +896,130 @@ 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 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 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 + 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 (deprecated, v2 only) */ + 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 } /** 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 +1033,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,17 +1047,32 @@ private constructor( this.systemPrompt = systemPrompt } - /** Logging verbosity level */ - fun verbose(verbose: Long) = verbose(JsonField.of(verbose)) + /** Logging verbosity level (0=quiet, 1=normal, 2=debug) */ + fun verbose(verbose: Verbose) = verbose(JsonField.of(verbose)) /** * Sets [Builder.verbose] to an arbitrary JSON value. * - * You should usually call [Builder.verbose] with a well-typed [Long] value instead. + * You should usually call [Builder.verbose] with a well-typed [Verbose] value instead. * This method is primarily for setting the field to an undocumented or not yet * supported value. */ - fun verbose(verbose: JsonField) = apply { this.verbose = verbose } + fun verbose(verbose: JsonField) = apply { this.verbose = verbose } + + /** Wait for captcha solves (deprecated, v2 only) */ + 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() @@ -762,21 +1100,24 @@ 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, + domSettleTimeoutMs, + experimental, selfHeal, systemPrompt, verbose, + waitForCaptchaSolves, additionalProperties.toMutableMap(), ) } @@ -788,13 +1129,17 @@ private constructor( return@apply } - browserbaseApiKey() - browserbaseProjectId() - domSettleTimeout() - model() + modelName() + actTimeoutMs() + browser().ifPresent { it.validate() } + browserbaseSessionCreateParams().ifPresent { it.validate() } + browserbaseSessionId() + domSettleTimeoutMs() + experimental() selfHeal() systemPrompt() - verbose() + verbose().ifPresent { it.validate() } + waitForCaptchaSolves() validated = true } @@ -814,13 +1159,17 @@ 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 (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) + (verbose.asKnown().getOrNull()?.validity() ?: 0) + + (if (waitForCaptchaSolves.asKnown().isPresent) 1 else 0) override fun equals(other: Any?): Boolean { if (this === other) { @@ -828,25 +1177,33 @@ 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 && + 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, + domSettleTimeoutMs, + experimental, selfHeal, systemPrompt, verbose, + waitForCaptchaSolves, additionalProperties, ) } @@ -854,22 +1211,6279 @@ 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, 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, + ) { + + @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 + + /** + * 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) } - return other is SessionStartParams && - body == other.body && - additionalHeaders == other.additionalHeaders && - additionalQueryParams == other.additionalQueryParams - } + @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)) - override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + /** + * 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}" + } + + /** Logging verbosity level (0=quiet, 1=normal, 2=debug) */ + class Verbose @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 _0 = of(0.0) + + @JvmField val _1 = of(1.0) + + @JvmField val _2 = of(2.0) + + @JvmStatic fun of(value: Double) = Verbose(JsonField.of(value)) + } + + /** An enum containing [Verbose]'s known values. */ + enum class Known { + _0, + _1, + _2, + } + + /** + * An enum containing [Verbose]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Verbose] 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 { + _0, + _1, + _2, + /** An enum member indicating that [Verbose] 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) { + _0 -> Value._0 + _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) { + _0 -> Known._0 + _1 -> Known._1 + _2 -> Known._2 + else -> throw StagehandInvalidDataException("Unknown Verbose: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * @throws StagehandInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asDouble(): Double = + _value().asNumber().getOrNull()?.toDouble() + ?: throw StagehandInvalidDataException("Value is not a Double") + + private var validated: Boolean = false + + fun validate(): Verbose = 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 Verbose && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** 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..e342e19 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,50 @@ 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 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 +78,8 @@ private constructor( * * The following fields are required: * ```java - * .available() - * .sessionId() + * .data() + * .success() * ``` */ @JvmStatic fun builder() = Builder() @@ -88,40 +88,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 +146,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 +167,8 @@ private constructor( return@apply } - available() - sessionId() + data().validate() + success() validated = true } @@ -190,8 +187,250 @@ 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 sessionId: JsonField, + private val cdpUrl: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("available") + @ExcludeMissing + available: JsonField = JsonMissing.of(), + @JsonProperty("sessionId") + @ExcludeMissing + sessionId: JsonField = JsonMissing.of(), + @JsonProperty("cdpUrl") @ExcludeMissing cdpUrl: JsonField = JsonMissing.of(), + ) : this(available, sessionId, cdpUrl, 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") + + /** + * 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") + + /** + * CDP WebSocket URL for connecting to the Browserbase cloud browser (present when + * available) + * + * @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") + + /** + * 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 [sessionId]. + * + * Unlike [sessionId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sessionId") @ExcludeMissing fun _sessionId(): JsonField = sessionId + + /** + * 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 + + @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() + * .sessionId() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Data]. */ + class Builder internal constructor() { + + private var available: JsonField? = null + private var sessionId: JsonField? = null + private var cdpUrl: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(data: Data) = apply { + available = data.available + sessionId = data.sessionId + cdpUrl = data.cdpUrl + 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 } + + /** 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 } + + /** + * CDP WebSocket URL for connecting to the Browserbase cloud browser (present when + * available) + */ + fun cdpUrl(cdpUrl: String?) = cdpUrl(JsonField.ofNullable(cdpUrl)) + + /** Alias for calling [Builder.cdpUrl] with `cdpUrl.orElse(null)`. */ + fun cdpUrl(cdpUrl: Optional) = cdpUrl(cdpUrl.getOrNull()) + + /** + * 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 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() + * .sessionId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Data = + Data( + checkRequired("available", available), + checkRequired("sessionId", sessionId), + cdpUrl, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Data = apply { + if (validated) { + return@apply + } + + available() + sessionId() + cdpUrl() + 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 (sessionId.asKnown().isPresent) 1 else 0) + + (if (cdpUrl.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Data && + available == other.available && + sessionId == other.sessionId && + cdpUrl == other.cdpUrl && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(available, sessionId, cdpUrl, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Data{available=$available, sessionId=$sessionId, cdpUrl=$cdpUrl, additionalProperties=$additionalProperties}" + } override fun equals(other: Any?): Boolean { if (this === other) { @@ -199,15 +438,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..a123e6a --- /dev/null +++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/StreamEvent.kt @@ -0,0 +1,1178 @@ +// 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`. Key + * order: data (with status first), type, id. + */ +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..c76617d 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,18 @@ 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") + .backendNodeId(0.0) + .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.backendNodeId()).contains(0.0) + assertThat(action.method()).contains("click") } @Test @@ -32,11 +33,11 @@ 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") + .backendNodeId(0.0) + .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..fc5ea48 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,68 @@ 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") + .provider(ModelConfig.ModelConfigObject.Provider.OPENAI) .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") + .provider(ModelConfig.ModelConfigObject.Provider.OPENAI) + .build() + ) val roundtrippedModelConfig = jsonMapper.readValue( @@ -44,4 +73,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..52cc63c 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,52 @@ 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( + SessionActResponse.Data.Result.Action.builder() + .description("Click the submit button") + .selector("[data-testid='submit-button']") + .addArgument("Hello World") + .backendNodeId(0.0) + .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( + SessionActResponse.Data.Result.Action.builder() + .description("Click the submit button") + .selector("[data-testid='submit-button']") + .addArgument("Hello World") + .backendNodeId(0.0) + .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 +67,27 @@ 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( + SessionActResponse.Data.Result.Action.builder() + .description("Click the submit button") + .selector("[data-testid='submit-button']") + .addArgument("Hello World") + .backendNodeId(0.0) + .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..8869791 --- /dev/null +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteParamsTest.kt @@ -0,0 +1,203 @@ +// 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") + .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) + .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") + .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) + .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") + .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) + .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") + .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) + .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..938fff3 --- /dev/null +++ b/stagehand-java-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveResponseTest.kt @@ -0,0 +1,81 @@ +// 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( + SessionObserveResponse.Data.Result.builder() + .description("Click the submit button") + .selector("[data-testid='submit-button']") + .addArgument("Hello World") + .backendNodeId(0.0) + .method("click") + .build() + ) + .actionId("actionId") + .build() + ) + .success(true) + .build() + + assertThat(sessionObserveResponse.data()) + .isEqualTo( + SessionObserveResponse.Data.builder() + .addResult( + SessionObserveResponse.Data.Result.builder() + .description("Click the submit button") + .selector("[data-testid='submit-button']") + .addArgument("Hello World") + .backendNodeId(0.0) + .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( + SessionObserveResponse.Data.Result.builder() + .description("Click the submit button") + .selector("[data-testid='submit-button']") + .addArgument("Hello World") + .backendNodeId(0.0) + .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..765a962 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,623 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) + .selfHeal(true) + .systemPrompt("systemPrompt") + .verbose(SessionStartParams.Verbose._1) + .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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .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(0.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.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.verbose()).contains(SessionStartParams.Verbose._1) + 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..0ae6883 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) + .sessionId("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .cdpUrl("wss://connect.browserbase.com/?signingKey=abc123") + .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) + .sessionId("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .cdpUrl("wss://connect.browserbase.com/?signingKey=abc123") + .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) + .sessionId("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .cdpUrl("wss://connect.browserbase.com/?signingKey=abc123") + .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..f704c1a 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,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -104,13 +254,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -134,13 +433,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -164,13 +612,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -194,13 +791,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -224,13 +970,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -254,13 +1149,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -284,13 +1328,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -314,13 +1507,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -344,13 +1686,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -374,13 +1865,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -404,13 +2044,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -434,13 +2223,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -464,13 +2402,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -494,13 +2581,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -524,13 +2760,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .build() ) } @@ -552,13 +2937,162 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .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..2243046 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,151 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .waitForCaptchaSolves(true) .putAdditionalHeader("Secret-Header", "42") .putAdditionalQueryParam("secret_query_param", "42") .putAdditionalBodyProperty("secretProperty", JsonValue.from("42")) @@ -77,24 +215,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..f9bc4be 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,28 @@ 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") + .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) .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 +173,52 @@ 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") + .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) + .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 +234,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 +260,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 +317,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 +351,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 +427,154 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .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..1bad240 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,28 @@ 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") + .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) .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 +170,52 @@ 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") + .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) + .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 +231,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 +256,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 +313,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 +346,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 +421,154 @@ 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(0.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") + .domSettleTimeoutMs(5000.0) + .experimental(true) .selfHeal(true) .systemPrompt("systemPrompt") - .verbose(1L) + .verbose(SessionStartParams.Verbose._1) + .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..f3e08fa 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,11 @@ 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") + .backendNodeId(0.0) + .method("click") .build() val roundtrippedAction = @@ -75,19 +75,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) } }