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 2790605..d90df61 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-e6a9dca1a93568e403ac72128d86f30c8c3f1336d4b67017d7e61b1836f10f47.yml -openapi_spec_hash: ef01e0649bb0e283df0aa81c369649df -config_hash: 88e87ba7021be93d267ecfc8f5e6b891 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-4fb17cafc413ae3d575e3268602b01d2d0e9ebeb734a41b6086b3353ff0d2523.yml +openapi_spec_hash: 8d48d8564849246f6f14d900c6c5f60c +config_hash: 5c69fb596588b8ace08203858518c149 diff --git a/CHANGELOG.md b/CHANGELOG.md index a77936c..13da95d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # Changelog +## 0.3.0 (2025-12-20) + +Full Changelog: [v0.2.0...v0.3.0](https://github.com/browserbase/stagehand-kotlin/compare/v0.2.0...v0.3.0) + +### Features + +* **api:** manual updates ([e4a62fb](https://github.com/browserbase/stagehand-kotlin/commit/e4a62fb0ab12733c844757cbe8ab9894f6c8c8a1)) +* **api:** manual updates ([9480988](https://github.com/browserbase/stagehand-kotlin/commit/9480988b36974e37584a16a2173af3072840d4b8)) +* **api:** manual updates ([d0d4e1b](https://github.com/browserbase/stagehand-kotlin/commit/d0d4e1b22d4fa7a4ea49333800a4c5137659f5e6)) +* **api:** manual updates ([e631708](https://github.com/browserbase/stagehand-kotlin/commit/e63170897295bc07dc47d493573c1290eb26e5f1)) +* **api:** manual updates ([1697e14](https://github.com/browserbase/stagehand-kotlin/commit/1697e144099d3d39c43d1b2ad6ab830e9c4267da)) + + +### Chores + +* **internal:** codegen related update ([c6b00cb](https://github.com/browserbase/stagehand-kotlin/commit/c6b00cb1704aee4abf1aade497aed8270e250edc)) +* **internal:** codegen related update ([3b4a6f0](https://github.com/browserbase/stagehand-kotlin/commit/3b4a6f0749d57d3a817722988351a2899dca5199)) +* **internal:** codegen related update ([5531737](https://github.com/browserbase/stagehand-kotlin/commit/55317375b13e137f0ae5ebd5fcc05f1393bdf633)) +* **internal:** codegen related update ([842e54f](https://github.com/browserbase/stagehand-kotlin/commit/842e54f32d89d64633746138a28a1947c2f7aaf4)) + + +### Documentation + +* add more examples ([16aa06e](https://github.com/browserbase/stagehand-kotlin/commit/16aa06e0a826df71165130395e41f6fad0a4f708)) + ## 0.2.0 (2025-12-17) Full Changelog: [v0.1.0...v0.2.0](https://github.com/browserbase/stagehand-kotlin/compare/v0.1.0...v0.2.0) diff --git a/README.md b/README.md index a4eea7e..edd7eaf 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ -[![Maven Central](https://img.shields.io/maven-central/v/com.browserbase.api/stagehand-kotlin)](https://central.sonatype.com/artifact/com.browserbase.api/stagehand-kotlin/0.2.0) -[![javadoc](https://javadoc.io/badge2/com.browserbase.api/stagehand-kotlin/0.2.0/javadoc.svg)](https://javadoc.io/doc/com.browserbase.api/stagehand-kotlin/0.2.0) +[![Maven Central](https://img.shields.io/maven-central/v/com.browserbase.api/stagehand-kotlin)](https://central.sonatype.com/artifact/com.browserbase.api/stagehand-kotlin/0.3.0) +[![javadoc](https://javadoc.io/badge2/com.browserbase.api/stagehand-kotlin/0.3.0/javadoc.svg)](https://javadoc.io/doc/com.browserbase.api/stagehand-kotlin/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). KDocs are available on [javadoc.io](https://javadoc.io/doc/com.browserbase.api/stagehand-kotlin/0.2.0). +The REST API documentation can be found on [docs.stagehand.dev](https://docs.stagehand.dev). KDocs are available on [javadoc.io](https://javadoc.io/doc/com.browserbase.api/stagehand-kotlin/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-kotlin:0.2.0") +implementation("com.browserbase.api:stagehand-kotlin:0.3.0") ``` ### Maven @@ -33,7 +33,7 @@ implementation("com.browserbase.api:stagehand-kotlin:0.2.0") com.browserbase.api stagehand-kotlin - 0.2.0 + 0.3.0 ``` @@ -188,6 +188,21 @@ val response: SessionActResponse = client.sessions().act(params) The asynchronous client supports the same options as the synchronous one, except most methods are [suspending](https://kotlinlang.org/docs/coroutines-guide.html). +## 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-kotlin-core/src/main/kotlin/com/browserbase/api/core/http/StreamResponse.kt) for synchronous clients: + +```kotlin +client.sessions().actStreaming(params).use { response -> + response.asSequence().forEach { println(it) } + println("No more chunks!") +} +``` + ## Raw responses The SDK defines methods that deserialize responses into instances of Kotlin classes. However, these methods don't provide access to the response headers, status code, or the raw response body. @@ -234,6 +249,8 @@ The SDK throws custom unchecked exception types: | 5xx | [`InternalServerException`](stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/errors/InternalServerException.kt) | | others | [`UnexpectedStatusCodeException`](stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/errors/UnexpectedStatusCodeException.kt) | + [`SseException`](stagehand-kotlin-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-kotlin-core/src/main/kotlin/com/browserbase/api/errors/StagehandIoException.kt): I/O networking errors. - [`StagehandRetryableException`](stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/errors/StagehandRetryableException.kt): Generic error indicating a failure that could be retried by the client. diff --git a/build.gradle.kts b/build.gradle.kts index b5a0b67..0f0fb80 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/buildSrc/src/main/kotlin/stagehand.publish.gradle.kts b/buildSrc/src/main/kotlin/stagehand.publish.gradle.kts index f1d4d88..d6f4029 100644 --- a/buildSrc/src/main/kotlin/stagehand.publish.gradle.kts +++ b/buildSrc/src/main/kotlin/stagehand.publish.gradle.kts @@ -10,7 +10,7 @@ configure { pom { name.set("Stagehand API") - description.set("Stagehand SDK for AI browser automation [ALPHA].") + description.set("Stagehand SDK for AI browser automation [ALPHA]. This API allows clients to\nexecute browser automation tasks remotely on the Browserbase cloud.\n\nAll endpoints except /sessions/start require an active session ID. Responses are\nstreamed using Server-Sent Events (SSE) when the `x-stream-response: true`\nheader is provided.\n\nThis SDK is currently ALPHA software and is not production ready! Please try it\nand give us your feedback, stay tuned for upcoming release announcements!") url.set("https://docs.stagehand.dev") licenses { diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/core/handlers/SseHandler.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/core/handlers/SseHandler.kt new file mode 100644 index 0000000..b4bbf80 --- /dev/null +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/core/handlers/SseHandler.kt @@ -0,0 +1,133 @@ +// File generated from our OpenAPI spec by Stainless. + +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 + +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 +} + +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-kotlin-core/src/main/kotlin/com/browserbase/api/core/handlers/StreamHandler.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/core/handlers/StreamHandler.kt new file mode 100644 index 0000000..75e8226 --- /dev/null +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/core/handlers/StreamHandler.kt @@ -0,0 +1,99 @@ +@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 + +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 asSequence(): Sequence = sequence + + 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-kotlin-core/src/main/kotlin/com/browserbase/api/core/http/SseMessage.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/core/http/SseMessage.kt new file mode 100644 index 0000000..531d5c4 --- /dev/null +++ b/stagehand-kotlin-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 { + 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-kotlin-core/src/main/kotlin/com/browserbase/api/errors/SseException.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/errors/SseException.kt new file mode 100644 index 0000000..dfbbdeb --- /dev/null +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/errors/SseException.kt @@ -0,0 +1,85 @@ +// 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 + +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: + * ```kotlin + * .statusCode() + * .headers() + * .body() + * ``` + */ + 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 + + 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 } + + /** + * Returns an immutable instance of [SseException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .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-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/Action.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/Action.kt index dc420a8..7dbc7a3 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/Action.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/Action.kt @@ -24,6 +24,7 @@ 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, ) { @@ -37,8 +38,11 @@ private constructor( @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, method, mutableMapOf()) + ) : this(description, selector, arguments, backendNodeId, method, mutableMapOf()) /** * Human-readable description of the action @@ -64,6 +68,14 @@ private constructor( */ fun arguments(): List? = arguments.getNullable("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(): Double? = backendNodeId.getNullable("backendNodeId") + /** * The method to execute (click, fill, etc.) * @@ -93,6 +105,15 @@ private constructor( */ @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]. * @@ -132,6 +153,7 @@ private 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() @@ -139,6 +161,7 @@ private constructor( description = action.description selector = action.selector arguments = action.arguments.map { it.toMutableList() } + backendNodeId = action.backendNodeId method = action.method additionalProperties = action.additionalProperties.toMutableMap() } @@ -192,6 +215,20 @@ private constructor( } } + /** 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)) @@ -240,6 +277,7 @@ private constructor( checkRequired("description", description), checkRequired("selector", selector), (arguments ?: JsonMissing.of()).map { it.toImmutable() }, + backendNodeId, method, additionalProperties.toMutableMap(), ) @@ -255,6 +293,7 @@ private constructor( description() selector() arguments() + backendNodeId() method() validated = true } @@ -276,6 +315,7 @@ private constructor( (if (description.asKnown() == null) 0 else 1) + (if (selector.asKnown() == null) 0 else 1) + (arguments.asKnown()?.size ?: 0) + + (if (backendNodeId.asKnown() == null) 0 else 1) + (if (method.asKnown() == null) 0 else 1) override fun equals(other: Any?): Boolean { @@ -287,16 +327,17 @@ private constructor( 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, method, additionalProperties) + Objects.hash(description, selector, arguments, backendNodeId, method, additionalProperties) } override fun hashCode(): Int = hashCode override fun toString() = - "Action{description=$description, selector=$selector, arguments=$arguments, method=$method, additionalProperties=$additionalProperties}" + "Action{description=$description, selector=$selector, arguments=$arguments, backendNodeId=$backendNodeId, method=$method, additionalProperties=$additionalProperties}" } diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/ModelConfig.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/ModelConfig.kt index 426b552..10629da 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/ModelConfig.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/ModelConfig.kt @@ -4,6 +4,7 @@ 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 @@ -26,33 +27,44 @@ import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import java.util.Collections import java.util.Objects +/** + * 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 private constructor( - private val string: String? = null, - private val unionMember1: UnionMember1? = null, + private val name: String? = null, + private val modelConfigObject: ModelConfigObject? = null, private val _json: JsonValue? = null, ) { - fun string(): String? = string + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') + */ + fun name(): String? = name - fun unionMember1(): UnionMember1? = unionMember1 + fun modelConfigObject(): ModelConfigObject? = modelConfigObject - fun isString(): Boolean = string != null + fun isName(): Boolean = name != null - fun isUnionMember1(): Boolean = unionMember1 != null + fun isModelConfigObject(): Boolean = modelConfigObject != null - fun asString(): String = string.getOrThrow("string") + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') + */ + fun asName(): String = name.getOrThrow("name") - fun asUnionMember1(): UnionMember1 = unionMember1.getOrThrow("unionMember1") + fun asModelConfigObject(): ModelConfigObject = modelConfigObject.getOrThrow("modelConfigObject") fun _json(): JsonValue? = _json fun accept(visitor: Visitor): T = when { - string != null -> visitor.visitString(string) - unionMember1 != null -> visitor.visitUnionMember1(unionMember1) + name != null -> visitor.visitName(name) + modelConfigObject != null -> visitor.visitModelConfigObject(modelConfigObject) else -> visitor.unknown(_json) } @@ -65,10 +77,10 @@ private constructor( accept( object : Visitor { - override fun visitString(string: String) {} + override fun visitName(name: String) {} - override fun visitUnionMember1(unionMember1: UnionMember1) { - unionMember1.validate() + override fun visitModelConfigObject(modelConfigObject: ModelConfigObject) { + modelConfigObject.validate() } } ) @@ -91,9 +103,10 @@ private constructor( internal fun validity(): Int = accept( object : Visitor { - override fun visitString(string: String) = 1 + override fun visitName(name: String) = 1 - override fun visitUnionMember1(unionMember1: UnionMember1) = unionMember1.validity() + override fun visitModelConfigObject(modelConfigObject: ModelConfigObject) = + modelConfigObject.validity() override fun unknown(json: JsonValue?) = 0 } @@ -104,24 +117,31 @@ private constructor( return true } - return other is ModelConfig && string == other.string && unionMember1 == other.unionMember1 + return other is ModelConfig && + name == other.name && + modelConfigObject == other.modelConfigObject } - override fun hashCode(): Int = Objects.hash(string, unionMember1) + override fun hashCode(): Int = Objects.hash(name, modelConfigObject) override fun toString(): String = when { - string != null -> "ModelConfig{string=$string}" - unionMember1 != null -> "ModelConfig{unionMember1=$unionMember1}" + name != null -> "ModelConfig{name=$name}" + modelConfigObject != null -> "ModelConfig{modelConfigObject=$modelConfigObject}" _json != null -> "ModelConfig{_unknown=$_json}" else -> throw IllegalStateException("Invalid ModelConfig") } companion object { - fun ofString(string: String) = ModelConfig(string = string) + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') + */ + fun ofName(name: String) = ModelConfig(name = name) - fun ofUnionMember1(unionMember1: UnionMember1) = ModelConfig(unionMember1 = unionMember1) + fun ofModelConfigObject(modelConfigObject: ModelConfigObject) = + ModelConfig(modelConfigObject = modelConfigObject) } /** @@ -129,9 +149,13 @@ private constructor( */ interface Visitor { - fun visitString(string: String): T + /** + * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', + * 'anthropic/claude-4.5-opus') + */ + fun visitName(name: String): T - fun visitUnionMember1(unionMember1: UnionMember1): T + fun visitModelConfigObject(modelConfigObject: ModelConfigObject): T /** * Maps an unknown variant of [ModelConfig] to a value of type [T]. @@ -154,11 +178,11 @@ private constructor( val bestMatches = sequenceOf( - tryDeserialize(node, jacksonTypeRef())?.let { - ModelConfig(unionMember1 = it, _json = json) + tryDeserialize(node, jacksonTypeRef())?.let { + ModelConfig(modelConfigObject = it, _json = json) }, tryDeserialize(node, jacksonTypeRef())?.let { - ModelConfig(string = it, _json = json) + ModelConfig(name = it, _json = json) }, ) .filterNotNull() @@ -184,20 +208,21 @@ private constructor( provider: SerializerProvider, ) { when { - value.string != null -> generator.writeObject(value.string) - value.unionMember1 != null -> generator.writeObject(value.unionMember1) + value.name != null -> generator.writeObject(value.name) + value.modelConfigObject != null -> generator.writeObject(value.modelConfigObject) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid ModelConfig") } } } - class UnionMember1 + 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, ) { @@ -208,26 +233,43 @@ private constructor( modelName: JsonField = JsonMissing.of(), @JsonProperty("apiKey") @ExcludeMissing apiKey: JsonField = JsonMissing.of(), @JsonProperty("baseURL") @ExcludeMissing baseUrl: JsonField = JsonMissing.of(), - ) : this(modelName, apiKey, baseUrl, mutableMapOf()) + @JsonProperty("provider") + @ExcludeMissing + provider: JsonField = JsonMissing.of(), + ) : this(modelName, apiKey, baseUrl, provider, mutableMapOf()) /** + * Model name string without prefix (e.g., 'gpt-5-nano', 'claude-4.5-opus') + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is * unexpectedly missing or null (e.g. if the server responded with an unexpected value). */ fun modelName(): String = modelName.getRequired("modelName") /** + * API key for the model provider + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ fun apiKey(): String? = apiKey.getNullable("apiKey") /** + * Base URL for the model provider + * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). */ fun baseUrl(): String? = baseUrl.getNullable("baseURL") + /** + * 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(): Provider? = provider.getNullable("provider") + /** * Returns the raw JSON value of [modelName]. * @@ -249,6 +291,13 @@ private constructor( */ @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl + /** + * Returns the raw JSON value of [provider]. + * + * Unlike [provider], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonField = provider + @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { additionalProperties.put(key, value) @@ -264,7 +313,7 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of [UnionMember1]. + * Returns a mutable builder for constructing an instance of [ModelConfigObject]. * * The following fields are required: * ```kotlin @@ -274,21 +323,24 @@ private constructor( fun builder() = Builder() } - /** A builder for [UnionMember1]. */ + /** 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() - internal fun from(unionMember1: UnionMember1) = apply { - modelName = unionMember1.modelName - apiKey = unionMember1.apiKey - baseUrl = unionMember1.baseUrl - additionalProperties = unionMember1.additionalProperties.toMutableMap() + internal fun from(modelConfigObject: ModelConfigObject) = apply { + modelName = modelConfigObject.modelName + apiKey = modelConfigObject.apiKey + baseUrl = modelConfigObject.baseUrl + provider = modelConfigObject.provider + additionalProperties = modelConfigObject.additionalProperties.toMutableMap() } + /** Model name string without prefix (e.g., 'gpt-5-nano', 'claude-4.5-opus') */ fun modelName(modelName: String) = modelName(JsonField.of(modelName)) /** @@ -300,6 +352,7 @@ private constructor( */ fun modelName(modelName: JsonField) = apply { this.modelName = modelName } + /** API key for the model provider */ fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey)) /** @@ -311,6 +364,7 @@ private constructor( */ fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey } + /** Base URL for the model provider */ fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl)) /** @@ -322,6 +376,18 @@ private constructor( */ 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) @@ -342,7 +408,7 @@ private constructor( } /** - * Returns an immutable instance of [UnionMember1]. + * Returns an immutable instance of [ModelConfigObject]. * * Further updates to this [Builder] will not mutate the returned instance. * @@ -353,18 +419,19 @@ private constructor( * * @throws IllegalStateException if any required field is unset. */ - fun build(): UnionMember1 = - UnionMember1( + fun build(): ModelConfigObject = + ModelConfigObject( checkRequired("modelName", modelName), apiKey, baseUrl, + provider, additionalProperties.toMutableMap(), ) } private var validated: Boolean = false - fun validate(): UnionMember1 = apply { + fun validate(): ModelConfigObject = apply { if (validated) { return@apply } @@ -372,6 +439,7 @@ private constructor( modelName() apiKey() baseUrl() + provider()?.validate() validated = true } @@ -392,27 +460,170 @@ private constructor( internal fun validity(): Int = (if (modelName.asKnown() == null) 0 else 1) + (if (apiKey.asKnown() == null) 0 else 1) + - (if (baseUrl.asKnown() == null) 0 else 1) + (if (baseUrl.asKnown() == null) 0 else 1) + + (provider.asKnown()?.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 { + + val OPENAI = of("openai") + + val ANTHROPIC = of("anthropic") + + val GOOGLE = of("google") + + val MICROSOFT = of("microsoft") + + 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() ?: throw 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. + */ + 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 UnionMember1 && + return other is ModelConfigObject && modelName == other.modelName && apiKey == other.apiKey && baseUrl == other.baseUrl && + provider == other.provider && additionalProperties == other.additionalProperties } private val hashCode: Int by lazy { - Objects.hash(modelName, apiKey, baseUrl, additionalProperties) + Objects.hash(modelName, apiKey, baseUrl, provider, additionalProperties) } override fun hashCode(): Int = hashCode override fun toString() = - "UnionMember1{modelName=$modelName, apiKey=$apiKey, baseUrl=$baseUrl, additionalProperties=$additionalProperties}" + "ModelConfigObject{modelName=$modelName, apiKey=$apiKey, baseUrl=$baseUrl, provider=$provider, additionalProperties=$additionalProperties}" } } diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActParams.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActParams.kt index 9649ae7..e0860c1 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActParams.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActParams.kt @@ -11,7 +11,6 @@ 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 @@ -194,7 +193,7 @@ private constructor( fun input(string: String) = apply { body.input(string) } /** Alias for calling [input] with `Input.ofAction(action)`. */ - fun input(action: Input.ActionInput) = apply { body.input(action) } + fun input(action: Action) = apply { body.input(action) } /** Target frame ID for the action */ fun frameId(frameId: String) = apply { body.frameId(frameId) } @@ -495,7 +494,7 @@ private constructor( fun input(string: String) = input(Input.ofString(string)) /** Alias for calling [input] with `Input.ofAction(action)`. */ - fun input(action: Input.ActionInput) = input(Input.ofAction(action)) + fun input(action: Action) = input(Input.ofAction(action)) /** Target frame ID for the action */ fun frameId(frameId: String) = frameId(JsonField.of(frameId)) @@ -620,14 +619,14 @@ private constructor( class Input private constructor( private val string: String? = null, - private val action: ActionInput? = null, + private val action: Action? = null, private val _json: JsonValue? = null, ) { fun string(): String? = string /** Action object returned by observe and used by act */ - fun action(): ActionInput? = action + fun action(): Action? = action fun isString(): Boolean = string != null @@ -636,7 +635,7 @@ private constructor( fun asString(): String = string.getOrThrow("string") /** Action object returned by observe and used by act */ - fun asAction(): ActionInput = action.getOrThrow("action") + fun asAction(): Action = action.getOrThrow("action") fun _json(): JsonValue? = _json @@ -658,7 +657,7 @@ private constructor( object : Visitor { override fun visitString(string: String) {} - override fun visitAction(action: ActionInput) { + override fun visitAction(action: Action) { action.validate() } } @@ -685,7 +684,7 @@ private constructor( object : Visitor { override fun visitString(string: String) = 1 - override fun visitAction(action: ActionInput) = action.validity() + override fun visitAction(action: Action) = action.validity() override fun unknown(json: JsonValue?) = 0 } @@ -714,7 +713,7 @@ private constructor( fun ofString(string: String) = Input(string = string) /** Action object returned by observe and used by act */ - fun ofAction(action: ActionInput) = Input(action = action) + fun ofAction(action: Action) = Input(action = action) } /** An interface that defines how to map each variant of [Input] to a value of type [T]. */ @@ -723,7 +722,7 @@ private constructor( fun visitString(string: String): T /** Action object returned by observe and used by act */ - fun visitAction(action: ActionInput): T + fun visitAction(action: Action): T /** * Maps an unknown variant of [Input] to a value of type [T]. @@ -747,7 +746,7 @@ private constructor( val bestMatches = sequenceOf( - tryDeserialize(node, jacksonTypeRef())?.let { + tryDeserialize(node, jacksonTypeRef())?.let { Input(action = it, _json = json) }, tryDeserialize(node, jacksonTypeRef())?.let { @@ -785,309 +784,6 @@ private constructor( } } } - - /** Action object returned by observe and used by act */ - class ActionInput - @JsonCreator(mode = JsonCreator.Mode.DISABLED) - private constructor( - private val description: JsonField, - private val selector: JsonField, - private val arguments: 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("method") @ExcludeMissing method: JsonField = JsonMissing.of(), - ) : this(description, selector, arguments, 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(): List? = arguments.getNullable("arguments") - - /** - * 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(): String? = method.getNullable("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 [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 [ActionInput]. - * - * The following fields are required: - * ```kotlin - * .description() - * .selector() - * ``` - */ - fun builder() = Builder() - } - - /** A builder for [ActionInput]. */ - class Builder internal constructor() { - - private var description: JsonField? = null - private var selector: JsonField? = null - private var arguments: JsonField>? = null - private var method: JsonField = JsonMissing.of() - private var additionalProperties: MutableMap = mutableMapOf() - - internal fun from(actionInput: ActionInput) = apply { - description = actionInput.description - selector = actionInput.selector - arguments = actionInput.arguments.map { it.toMutableList() } - method = actionInput.method - additionalProperties = actionInput.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) - } - } - - /** 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 [ActionInput]. - * - * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```kotlin - * .description() - * .selector() - * ``` - * - * @throws IllegalStateException if any required field is unset. - */ - fun build(): ActionInput = - ActionInput( - checkRequired("description", description), - checkRequired("selector", selector), - (arguments ?: JsonMissing.of()).map { it.toImmutable() }, - method, - additionalProperties.toMutableMap(), - ) - } - - private var validated: Boolean = false - - fun validate(): ActionInput = apply { - if (validated) { - return@apply - } - - description() - selector() - arguments() - 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. - */ - internal fun validity(): Int = - (if (description.asKnown() == null) 0 else 1) + - (if (selector.asKnown() == null) 0 else 1) + - (arguments.asKnown()?.size ?: 0) + - (if (method.asKnown() == null) 0 else 1) - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return other is ActionInput && - description == other.description && - selector == other.selector && - arguments == other.arguments && - method == other.method && - additionalProperties == other.additionalProperties - } - - private val hashCode: Int by lazy { - Objects.hash(description, selector, arguments, method, additionalProperties) - } - - override fun hashCode(): Int = hashCode - - override fun toString() = - "ActionInput{description=$description, selector=$selector, arguments=$arguments, method=$method, additionalProperties=$additionalProperties}" - } } class Options @@ -1109,6 +805,9 @@ private constructor( ) : 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). */ @@ -1186,6 +885,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)) /** @@ -1197,12 +900,14 @@ private constructor( */ fun model(model: JsonField) = apply { this.model = model } - /** Alias for calling [model] with `ModelConfig.ofString(string)`. */ - fun model(string: String) = model(ModelConfig.ofString(string)) + /** Alias for calling [model] with `ModelConfig.ofName(name)`. */ + fun model(name: String) = model(ModelConfig.ofName(name)) - /** Alias for calling [model] with `ModelConfig.ofUnionMember1(unionMember1)`. */ - fun model(unionMember1: ModelConfig.UnionMember1) = - model(ModelConfig.ofUnionMember1(unionMember1)) + /** + * 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)) diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActResponse.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActResponse.kt index e10b0fe..b3e8813 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActResponse.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActResponse.kt @@ -2,7 +2,6 @@ package com.browserbase.api.models.sessions -import com.browserbase.api.core.Enum import com.browserbase.api.core.ExcludeMissing import com.browserbase.api.core.JsonField import com.browserbase.api.core.JsonMissing @@ -22,14 +21,14 @@ class SessionActResponse @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val data: JsonField, - private val success: 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(), + @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(), ) : this(data, success, mutableMapOf()) /** @@ -39,10 +38,12 @@ private constructor( 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(): Success = success.getRequired("success") + fun success(): Boolean = success.getRequired("success") /** * Returns the raw JSON value of [data]. @@ -56,7 +57,7 @@ private constructor( * * Unlike [success], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success + @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -88,7 +89,7 @@ private constructor( class Builder internal constructor() { private var data: JsonField? = null - private var success: JsonField? = null + private var success: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() internal fun from(sessionActResponse: SessionActResponse) = apply { @@ -107,15 +108,16 @@ private constructor( */ fun data(data: JsonField) = apply { this.data = data } - fun success(success: Success) = success(JsonField.of(success)) + /** 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 [Success] 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 success(success: JsonField) = apply { this.success = success } + fun success(success: JsonField) = apply { this.success = success } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -165,7 +167,7 @@ private constructor( } data().validate() - success().validate() + success() validated = true } @@ -183,7 +185,7 @@ private constructor( * Used for best match union deserialization. */ internal fun validity(): Int = - (data.asKnown()?.validity() ?: 0) + (success.asKnown()?.validity() ?: 0) + (data.asKnown()?.validity() ?: 0) + (if (success.asKnown() == null) 0 else 1) class Data @JsonCreator(mode = JsonCreator.Mode.DISABLED) @@ -638,6 +640,364 @@ private constructor( (if (message.asKnown() == null) 0 else 1) + (if (success.asKnown() == null) 0 else 1) + /** 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(): List? = arguments.getNullable("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(): Double? = backendNodeId.getNullable("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(): String? = method.getNullable("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: + * ```kotlin + * .description() + * .selector() + * ``` + */ + 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() + + 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: + * ```kotlin + * .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. + */ + internal fun validity(): Int = + (if (description.asKnown() == null) 0 else 1) + + (if (selector.asKnown() == null) 0 else 1) + + (arguments.asKnown()?.size ?: 0) + + (if (backendNodeId.asKnown() == null) 0 else 1) + + (if (method.asKnown() == null) 0 else 1) + + 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 @@ -680,122 +1040,6 @@ private constructor( "Data{result=$result, actionId=$actionId, additionalProperties=$additionalProperties}" } - class Success @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 { - - val TRUE = of(true) - - fun of(value: Boolean) = Success(JsonField.of(value)) - } - - /** An enum containing [Success]'s known values. */ - enum class Known { - TRUE - } - - /** - * An enum containing [Success]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Success] 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, - /** An enum member indicating that [Success] 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 - 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 - else -> throw StagehandInvalidDataException("Unknown Success: $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 asBoolean(): Boolean = - _value().asBoolean() ?: throw StagehandInvalidDataException("Value is not a Boolean") - - private var validated: Boolean = false - - fun validate(): Success = 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. - */ - 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 Success && value == other.value - } - - override fun hashCode() = value.hashCode() - - override fun toString() = value.toString() - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndResponse.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndResponse.kt index 5168fc3..09c9d2f 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndResponse.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndResponse.kt @@ -2,7 +2,6 @@ package com.browserbase.api.models.sessions -import com.browserbase.api.core.Enum import com.browserbase.api.core.ExcludeMissing import com.browserbase.api.core.JsonField import com.browserbase.api.core.JsonMissing @@ -19,27 +18,29 @@ import java.util.Objects class SessionEndResponse @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val success: JsonField, + private val success: JsonField, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of() + @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of() ) : this(success, mutableMapOf()) /** + * 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(): Success = success.getRequired("success") + fun success(): Boolean = success.getRequired("success") /** * 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 + @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -69,7 +70,7 @@ private constructor( /** A builder for [SessionEndResponse]. */ class Builder internal constructor() { - private var success: JsonField? = null + private var success: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() internal fun from(sessionEndResponse: SessionEndResponse) = apply { @@ -77,15 +78,16 @@ private constructor( additionalProperties = sessionEndResponse.additionalProperties.toMutableMap() } - fun success(success: Success) = success(JsonField.of(success)) + /** 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 [Success] 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 success(success: JsonField) = apply { this.success = success } + fun success(success: JsonField) = apply { this.success = success } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -132,7 +134,7 @@ private constructor( return@apply } - success().validate() + success() validated = true } @@ -149,123 +151,7 @@ private constructor( * * Used for best match union deserialization. */ - internal fun validity(): Int = (success.asKnown()?.validity() ?: 0) - - class Success @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 { - - val TRUE = of(true) - - fun of(value: Boolean) = Success(JsonField.of(value)) - } - - /** An enum containing [Success]'s known values. */ - enum class Known { - TRUE - } - - /** - * An enum containing [Success]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Success] 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, - /** An enum member indicating that [Success] 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 - 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 - else -> throw StagehandInvalidDataException("Unknown Success: $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 asBoolean(): Boolean = - _value().asBoolean() ?: throw StagehandInvalidDataException("Value is not a Boolean") - - private var validated: Boolean = false - - fun validate(): Success = 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. - */ - 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 Success && value == other.value - } - - override fun hashCode() = value.hashCode() - - override fun toString() = value.toString() - } + internal fun validity(): Int = (if (success.asKnown() == null) 0 else 1) override fun equals(other: Any?): Boolean { if (this === other) { diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteParams.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteParams.kt index 6a8face..c56549c 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteParams.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteParams.kt @@ -614,6 +614,7 @@ private constructor( private constructor( private val cua: JsonField, private val model: JsonField, + private val provider: JsonField, private val systemPrompt: JsonField, private val additionalProperties: MutableMap, ) { @@ -622,10 +623,13 @@ private constructor( private constructor( @JsonProperty("cua") @ExcludeMissing cua: JsonField = JsonMissing.of(), @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), + @JsonProperty("provider") + @ExcludeMissing + provider: JsonField = JsonMissing.of(), @JsonProperty("systemPrompt") @ExcludeMissing systemPrompt: JsonField = JsonMissing.of(), - ) : this(cua, model, systemPrompt, mutableMapOf()) + ) : this(cua, model, provider, systemPrompt, mutableMapOf()) /** * Enable Computer Use Agent mode @@ -636,11 +640,22 @@ private constructor( fun cua(): Boolean? = cua.getNullable("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(): ModelConfig? = model.getNullable("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(): Provider? = provider.getNullable("provider") + /** * Custom system prompt for the agent * @@ -663,6 +678,13 @@ private constructor( */ @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + /** + * Returns the raw JSON value of [provider]. + * + * Unlike [provider], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonField = provider + /** * Returns the raw JSON value of [systemPrompt]. * @@ -696,12 +718,14 @@ private constructor( private var cua: 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() internal fun from(agentConfig: AgentConfig) = apply { cua = agentConfig.cua model = agentConfig.model + provider = agentConfig.provider systemPrompt = agentConfig.systemPrompt additionalProperties = agentConfig.additionalProperties.toMutableMap() } @@ -718,6 +742,10 @@ private constructor( */ fun cua(cua: JsonField) = apply { this.cua = cua } + /** + * 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)) /** @@ -729,12 +757,26 @@ private constructor( */ fun model(model: JsonField) = apply { this.model = model } - /** Alias for calling [model] with `ModelConfig.ofString(string)`. */ - fun model(string: String) = model(ModelConfig.ofString(string)) + /** 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)) + + /** AI provider for the agent (legacy, use model: openai/gpt-5-nano instead) */ + fun provider(provider: Provider) = provider(JsonField.of(provider)) - /** Alias for calling [model] with `ModelConfig.ofUnionMember1(unionMember1)`. */ - fun model(unionMember1: ModelConfig.UnionMember1) = - model(ModelConfig.ofUnionMember1(unionMember1)) + /** + * 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 } /** Custom system prompt for the agent */ fun systemPrompt(systemPrompt: String) = systemPrompt(JsonField.of(systemPrompt)) @@ -775,7 +817,7 @@ private constructor( * Further updates to this [Builder] will not mutate the returned instance. */ fun build(): AgentConfig = - AgentConfig(cua, model, systemPrompt, additionalProperties.toMutableMap()) + AgentConfig(cua, model, provider, systemPrompt, additionalProperties.toMutableMap()) } private var validated: Boolean = false @@ -787,6 +829,7 @@ private constructor( cua() model()?.validate() + provider()?.validate() systemPrompt() validated = true } @@ -808,8 +851,150 @@ private constructor( internal fun validity(): Int = (if (cua.asKnown() == null) 0 else 1) + (model.asKnown()?.validity() ?: 0) + + (provider.asKnown()?.validity() ?: 0) + (if (systemPrompt.asKnown() == null) 0 else 1) + /** AI provider for the agent (legacy, use model: openai/gpt-5-nano 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 { + + val OPENAI = of("openai") + + val ANTHROPIC = of("anthropic") + + val GOOGLE = of("google") + + val MICROSOFT = of("microsoft") + + 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() ?: throw 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. + */ + 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 @@ -818,18 +1003,19 @@ private constructor( return other is AgentConfig && cua == other.cua && model == other.model && + provider == other.provider && systemPrompt == other.systemPrompt && additionalProperties == other.additionalProperties } private val hashCode: Int by lazy { - Objects.hash(cua, model, systemPrompt, additionalProperties) + Objects.hash(cua, model, provider, systemPrompt, additionalProperties) } override fun hashCode(): Int = hashCode override fun toString() = - "AgentConfig{cua=$cua, model=$model, systemPrompt=$systemPrompt, additionalProperties=$additionalProperties}" + "AgentConfig{cua=$cua, model=$model, provider=$provider, systemPrompt=$systemPrompt, additionalProperties=$additionalProperties}" } class ExecuteOptions diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteResponse.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteResponse.kt index f42572f..444c2a5 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteResponse.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExecuteResponse.kt @@ -2,7 +2,6 @@ package com.browserbase.api.models.sessions -import com.browserbase.api.core.Enum import com.browserbase.api.core.ExcludeMissing import com.browserbase.api.core.JsonField import com.browserbase.api.core.JsonMissing @@ -22,14 +21,14 @@ class SessionExecuteResponse @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val data: JsonField, - private val success: 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(), + @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(), ) : this(data, success, mutableMapOf()) /** @@ -39,10 +38,12 @@ private constructor( 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(): Success = success.getRequired("success") + fun success(): Boolean = success.getRequired("success") /** * Returns the raw JSON value of [data]. @@ -56,7 +57,7 @@ private constructor( * * Unlike [success], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success + @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -88,7 +89,7 @@ private constructor( class Builder internal constructor() { private var data: JsonField? = null - private var success: JsonField? = null + private var success: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() internal fun from(sessionExecuteResponse: SessionExecuteResponse) = apply { @@ -107,15 +108,16 @@ private constructor( */ fun data(data: JsonField) = apply { this.data = data } - fun success(success: Success) = success(JsonField.of(success)) + /** 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 [Success] 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 success(success: JsonField) = apply { this.success = success } + fun success(success: JsonField) = apply { this.success = success } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -165,7 +167,7 @@ private constructor( } data().validate() - success().validate() + success() validated = true } @@ -183,7 +185,7 @@ private constructor( * Used for best match union deserialization. */ internal fun validity(): Int = - (data.asKnown()?.validity() ?: 0) + (success.asKnown()?.validity() ?: 0) + (data.asKnown()?.validity() ?: 0) + (if (success.asKnown() == null) 0 else 1) class Data @JsonCreator(mode = JsonCreator.Mode.DISABLED) @@ -1622,122 +1624,6 @@ private constructor( override fun toString() = "Data{result=$result, additionalProperties=$additionalProperties}" } - class Success @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 { - - val TRUE = of(true) - - fun of(value: Boolean) = Success(JsonField.of(value)) - } - - /** An enum containing [Success]'s known values. */ - enum class Known { - TRUE - } - - /** - * An enum containing [Success]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Success] 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, - /** An enum member indicating that [Success] 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 - 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 - else -> throw StagehandInvalidDataException("Unknown Success: $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 asBoolean(): Boolean = - _value().asBoolean() ?: throw StagehandInvalidDataException("Value is not a Boolean") - - private var validated: Boolean = false - - fun validate(): Success = 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. - */ - 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 Success && value == other.value - } - - override fun hashCode() = value.hashCode() - - override fun toString() = value.toString() - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractParams.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractParams.kt index 15600fe..6bb32ec 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractParams.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractParams.kt @@ -645,6 +645,9 @@ private constructor( ) : 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). */ @@ -720,6 +723,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)) /** @@ -731,12 +738,14 @@ private constructor( */ fun model(model: JsonField) = apply { this.model = model } - /** Alias for calling [model] with `ModelConfig.ofString(string)`. */ - fun model(string: String) = model(ModelConfig.ofString(string)) + /** Alias for calling [model] with `ModelConfig.ofName(name)`. */ + fun model(name: String) = model(ModelConfig.ofName(name)) - /** Alias for calling [model] with `ModelConfig.ofUnionMember1(unionMember1)`. */ - fun model(unionMember1: ModelConfig.UnionMember1) = - model(ModelConfig.ofUnionMember1(unionMember1)) + /** + * 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)) diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractResponse.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractResponse.kt index 38ee91a..5c99f5a 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractResponse.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionExtractResponse.kt @@ -2,7 +2,6 @@ package com.browserbase.api.models.sessions -import com.browserbase.api.core.Enum import com.browserbase.api.core.ExcludeMissing import com.browserbase.api.core.JsonField import com.browserbase.api.core.JsonMissing @@ -20,14 +19,14 @@ class SessionExtractResponse @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val data: JsonField, - private val success: 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(), + @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(), ) : this(data, success, mutableMapOf()) /** @@ -37,10 +36,12 @@ private constructor( 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(): Success = success.getRequired("success") + fun success(): Boolean = success.getRequired("success") /** * Returns the raw JSON value of [data]. @@ -54,7 +55,7 @@ private constructor( * * Unlike [success], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success + @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -86,7 +87,7 @@ private constructor( class Builder internal constructor() { private var data: JsonField? = null - private var success: JsonField? = null + private var success: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() internal fun from(sessionExtractResponse: SessionExtractResponse) = apply { @@ -105,15 +106,16 @@ private constructor( */ fun data(data: JsonField) = apply { this.data = data } - fun success(success: Success) = success(JsonField.of(success)) + /** 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 [Success] 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 success(success: JsonField) = apply { this.success = success } + fun success(success: JsonField) = apply { this.success = success } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -163,7 +165,7 @@ private constructor( } data().validate() - success().validate() + success() validated = true } @@ -181,7 +183,7 @@ private constructor( * Used for best match union deserialization. */ internal fun validity(): Int = - (data.asKnown()?.validity() ?: 0) + (success.asKnown()?.validity() ?: 0) + (data.asKnown()?.validity() ?: 0) + (if (success.asKnown() == null) 0 else 1) class Data @JsonCreator(mode = JsonCreator.Mode.DISABLED) @@ -349,122 +351,6 @@ private constructor( "Data{result=$result, actionId=$actionId, additionalProperties=$additionalProperties}" } - class Success @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 { - - val TRUE = of(true) - - fun of(value: Boolean) = Success(JsonField.of(value)) - } - - /** An enum containing [Success]'s known values. */ - enum class Known { - TRUE - } - - /** - * An enum containing [Success]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Success] 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, - /** An enum member indicating that [Success] 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 - 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 - else -> throw StagehandInvalidDataException("Unknown Success: $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 asBoolean(): Boolean = - _value().asBoolean() ?: throw StagehandInvalidDataException("Value is not a Boolean") - - private var validated: Boolean = false - - fun validate(): Success = 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. - */ - 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 Success && value == other.value - } - - override fun hashCode() = value.hashCode() - - override fun toString() = value.toString() - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateParams.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateParams.kt index c61a8aa..ec1df26 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateParams.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateParams.kt @@ -71,6 +71,14 @@ private constructor( */ fun options(): Options? = 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(): Boolean? = body.streamResponse() + /** * Returns the raw JSON value of [url]. * @@ -92,6 +100,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. */ @@ -163,6 +178,7 @@ private constructor( * - [url] * - [frameId] * - [options] + * - [streamResponse] */ fun body(body: Body) = apply { this.body = body.toBuilder() } @@ -198,6 +214,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) } @@ -367,6 +397,7 @@ private constructor( private val url: JsonField, private val frameId: JsonField, private val options: JsonField, + private val streamResponse: JsonField, private val additionalProperties: MutableMap, ) { @@ -375,7 +406,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 @@ -399,6 +433,14 @@ private constructor( */ fun options(): Options? = options.getNullable("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(): Boolean? = streamResponse.getNullable("streamResponse") + /** * Returns the raw JSON value of [url]. * @@ -420,6 +462,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) @@ -451,12 +503,14 @@ 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() internal fun from(body: Body) = apply { url = body.url frameId = body.frameId options = body.options + streamResponse = body.streamResponse additionalProperties = body.additionalProperties.toMutableMap() } @@ -495,6 +549,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) @@ -531,6 +600,7 @@ private constructor( checkRequired("url", url), frameId, options, + streamResponse, additionalProperties.toMutableMap(), ) } @@ -545,6 +615,7 @@ private constructor( url() frameId() options()?.validate() + streamResponse() validated = true } @@ -565,7 +636,8 @@ private constructor( internal fun validity(): Int = (if (url.asKnown() == null) 0 else 1) + (if (frameId.asKnown() == null) 0 else 1) + - (options.asKnown()?.validity() ?: 0) + (options.asKnown()?.validity() ?: 0) + + (if (streamResponse.asKnown() == null) 0 else 1) override fun equals(other: Any?): Boolean { if (this === other) { @@ -576,17 +648,18 @@ 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 diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponse.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponse.kt index 18924d5..320e354 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponse.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponse.kt @@ -2,7 +2,6 @@ package com.browserbase.api.models.sessions -import com.browserbase.api.core.Enum import com.browserbase.api.core.ExcludeMissing import com.browserbase.api.core.JsonField import com.browserbase.api.core.JsonMissing @@ -20,14 +19,14 @@ class SessionNavigateResponse @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val data: JsonField, - private val success: 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(), + @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(), ) : this(data, success, mutableMapOf()) /** @@ -37,10 +36,12 @@ private constructor( 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(): Success = success.getRequired("success") + fun success(): Boolean = success.getRequired("success") /** * Returns the raw JSON value of [data]. @@ -54,7 +55,7 @@ private constructor( * * Unlike [success], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success + @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -86,7 +87,7 @@ private constructor( class Builder internal constructor() { private var data: JsonField? = null - private var success: JsonField? = null + private var success: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() internal fun from(sessionNavigateResponse: SessionNavigateResponse) = apply { @@ -105,15 +106,16 @@ private constructor( */ fun data(data: JsonField) = apply { this.data = data } - fun success(success: Success) = success(JsonField.of(success)) + /** 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 [Success] 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 success(success: JsonField) = apply { this.success = success } + fun success(success: JsonField) = apply { this.success = success } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -163,7 +165,7 @@ private constructor( } data().validate() - success().validate() + success() validated = true } @@ -181,7 +183,7 @@ private constructor( * Used for best match union deserialization. */ internal fun validity(): Int = - (data.asKnown()?.validity() ?: 0) + (success.asKnown()?.validity() ?: 0) + (data.asKnown()?.validity() ?: 0) + (if (success.asKnown() == null) 0 else 1) class Data @JsonCreator(mode = JsonCreator.Mode.DISABLED) @@ -349,122 +351,6 @@ private constructor( "Data{result=$result, actionId=$actionId, additionalProperties=$additionalProperties}" } - class Success @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 { - - val TRUE = of(true) - - fun of(value: Boolean) = Success(JsonField.of(value)) - } - - /** An enum containing [Success]'s known values. */ - enum class Known { - TRUE - } - - /** - * An enum containing [Success]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Success] 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, - /** An enum member indicating that [Success] 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 - 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 - else -> throw StagehandInvalidDataException("Unknown Success: $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 asBoolean(): Boolean = - _value().asBoolean() ?: throw StagehandInvalidDataException("Value is not a Boolean") - - private var validated: Boolean = false - - fun validate(): Success = 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. - */ - 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 Success && value == other.value - } - - override fun hashCode() = value.hashCode() - - override fun toString() = value.toString() - } - override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveParams.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveParams.kt index fc528f8..e357aa0 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveParams.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveParams.kt @@ -585,6 +585,9 @@ private constructor( ) : 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). */ @@ -660,6 +663,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)) /** @@ -671,12 +678,14 @@ private constructor( */ fun model(model: JsonField) = apply { this.model = model } - /** Alias for calling [model] with `ModelConfig.ofString(string)`. */ - fun model(string: String) = model(ModelConfig.ofString(string)) + /** Alias for calling [model] with `ModelConfig.ofName(name)`. */ + fun model(name: String) = model(ModelConfig.ofName(name)) - /** Alias for calling [model] with `ModelConfig.ofUnionMember1(unionMember1)`. */ - fun model(unionMember1: ModelConfig.UnionMember1) = - model(ModelConfig.ofUnionMember1(unionMember1)) + /** + * 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)) diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveResponse.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveResponse.kt index 760fff6..869bc64 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveResponse.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionObserveResponse.kt @@ -2,7 +2,6 @@ package com.browserbase.api.models.sessions -import com.browserbase.api.core.Enum import com.browserbase.api.core.ExcludeMissing import com.browserbase.api.core.JsonField import com.browserbase.api.core.JsonMissing @@ -22,14 +21,14 @@ class SessionObserveResponse @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val data: JsonField, - private val success: 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(), + @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(), ) : this(data, success, mutableMapOf()) /** @@ -39,10 +38,12 @@ private constructor( 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(): Success = success.getRequired("success") + fun success(): Boolean = success.getRequired("success") /** * Returns the raw JSON value of [data]. @@ -56,7 +57,7 @@ private constructor( * * Unlike [success], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success + @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -88,7 +89,7 @@ private constructor( class Builder internal constructor() { private var data: JsonField? = null - private var success: JsonField? = null + private var success: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() internal fun from(sessionObserveResponse: SessionObserveResponse) = apply { @@ -107,15 +108,16 @@ private constructor( */ fun data(data: JsonField) = apply { this.data = data } - fun success(success: Success) = success(JsonField.of(success)) + /** 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 [Success] 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 success(success: JsonField) = apply { this.success = success } + fun success(success: JsonField) = apply { this.success = success } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -165,7 +167,7 @@ private constructor( } data().validate() - success().validate() + success() validated = true } @@ -183,12 +185,12 @@ private constructor( * Used for best match union deserialization. */ internal fun validity(): Int = - (data.asKnown()?.validity() ?: 0) + (success.asKnown()?.validity() ?: 0) + (data.asKnown()?.validity() ?: 0) + (if (success.asKnown() == null) 0 else 1) class Data @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val result: JsonField>, + private val result: JsonField>, private val actionId: JsonField, private val additionalProperties: MutableMap, ) { @@ -197,7 +199,7 @@ private constructor( private constructor( @JsonProperty("result") @ExcludeMissing - result: JsonField> = JsonMissing.of(), + result: JsonField> = JsonMissing.of(), @JsonProperty("actionId") @ExcludeMissing actionId: JsonField = JsonMissing.of(), ) : this(result, actionId, mutableMapOf()) @@ -205,7 +207,7 @@ private constructor( * @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") + fun result(): List = result.getRequired("result") /** * Action ID for tracking @@ -220,7 +222,7 @@ private constructor( * * Unlike [result], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("result") @ExcludeMissing fun _result(): JsonField> = result + @JsonProperty("result") @ExcludeMissing fun _result(): JsonField> = result /** * Returns the raw JSON value of [actionId]. @@ -257,7 +259,7 @@ private constructor( /** A builder for [Data]. */ class Builder internal constructor() { - private var result: JsonField>? = null + private var result: JsonField>? = null private var actionId: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -267,25 +269,25 @@ private constructor( additionalProperties = data.additionalProperties.toMutableMap() } - fun result(result: List) = result(JsonField.of(result)) + 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 + * 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 { + fun result(result: JsonField>) = apply { this.result = result.map { it.toMutableList() } } /** - * Adds a single [Action] to [Builder.result]. + * Adds a single [Result] to [Builder.result]. * * @throws IllegalStateException if the field was previously set to a non-list. */ - fun addResult(result: Action) = apply { + fun addResult(result: Result) = apply { this.result = (this.result ?: JsonField.of(mutableListOf())).also { checkKnown("result", it).add(result) @@ -373,139 +375,376 @@ private constructor( (result.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + (if (actionId.asKnown() == null) 0 else 1) - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } + /** 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()) - return other is Data && - result == other.result && - actionId == other.actionId && - additionalProperties == other.additionalProperties - } + /** + * 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") - private val hashCode: Int by lazy { Objects.hash(result, actionId, additionalProperties) } + /** + * 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") - override fun hashCode(): Int = hashCode + /** + * 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(): List? = arguments.getNullable("arguments") - override fun toString() = - "Data{result=$result, actionId=$actionId, additionalProperties=$additionalProperties}" - } + /** + * 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(): Double? = backendNodeId.getNullable("backendNodeId") - class Success @JsonCreator private constructor(private val value: JsonField) : Enum { + /** + * 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(): String? = method.getNullable("method") - /** - * 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 + /** + * 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 - companion object { + /** + * 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 - val TRUE = of(true) + /** + * 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 - fun of(value: Boolean) = Success(JsonField.of(value)) - } + /** + * 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 - /** An enum containing [Success]'s known values. */ - enum class Known { - TRUE - } + /** + * 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 - /** - * An enum containing [Success]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Success] 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, - /** An enum member indicating that [Success] was instantiated with an unknown value. */ - _UNKNOWN, - } + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } - /** - * 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 - else -> Value._UNKNOWN + @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: + * ```kotlin + * .description() + * .selector() + * ``` + */ + fun builder() = Builder() } - /** - * 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 - else -> throw StagehandInvalidDataException("Unknown Success: $value") + /** 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() + + 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: + * ```kotlin + * .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(), + ) } - /** - * Returns this class instance's primitive wire representation. - * - * @throws StagehandInvalidDataException if this class instance's value does not have the - * expected primitive type. - */ - fun asBoolean(): Boolean = - _value().asBoolean() ?: throw StagehandInvalidDataException("Value is not a Boolean") + private var validated: Boolean = false - private var validated: Boolean = false + fun validate(): Result = apply { + if (validated) { + return@apply + } - fun validate(): Success = apply { - if (validated) { - return@apply + description() + selector() + arguments() + backendNodeId() + method() + validated = true } - known() - validated = true - } + fun isValid(): Boolean = + try { + validate() + true + } catch (e: StagehandInvalidDataException) { + false + } - fun isValid(): Boolean = - try { - validate() - true - } catch (e: StagehandInvalidDataException) { - false + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (description.asKnown() == null) 0 else 1) + + (if (selector.asKnown() == null) 0 else 1) + + (arguments.asKnown()?.size ?: 0) + + (if (backendNodeId.asKnown() == null) 0 else 1) + + (if (method.asKnown() == null) 0 else 1) + + 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 } - /** - * Returns a score indicating how many valid values are contained in this object - * recursively. - * - * Used for best match union deserialization. - */ - internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + 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 Success && value == other.value + return other is Data && + result == other.result && + actionId == other.actionId && + additionalProperties == other.additionalProperties } - override fun hashCode() = value.hashCode() + private val hashCode: Int by lazy { Objects.hash(result, actionId, additionalProperties) } + + override fun hashCode(): Int = hashCode - override fun toString() = value.toString() + override fun toString() = + "Data{result=$result, actionId=$actionId, additionalProperties=$additionalProperties}" } override fun equals(other: Any?): Boolean { diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartParams.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartParams.kt index 10b0891..1a333d4 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartParams.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartParams.kt @@ -70,7 +70,7 @@ private constructor( fun modelName(): String = body.modelName() /** - * Timeout in ms for act operations + * Timeout in ms for act operations (deprecated, v2 only) * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -98,12 +98,6 @@ private constructor( */ fun browserbaseSessionId(): String? = body.browserbaseSessionId() - /** - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the - * server responded with an unexpected value). - */ - fun debugDom(): Boolean? = body.debugDom() - /** * Timeout in ms to wait for DOM to settle * @@ -143,6 +137,8 @@ private constructor( fun verbose(): Verbose? = body.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). */ @@ -186,13 +182,6 @@ private constructor( */ fun _browserbaseSessionId(): JsonField = body._browserbaseSessionId() - /** - * Returns the raw JSON value of [debugDom]. - * - * Unlike [debugDom], this method doesn't throw if the JSON field has an unexpected type. - */ - fun _debugDom(): JsonField = body._debugDom() - /** * Returns the raw JSON value of [domSettleTimeoutMs]. * @@ -321,7 +310,7 @@ private constructor( */ fun modelName(modelName: JsonField) = apply { body.modelName(modelName) } - /** Timeout in ms for act operations */ + /** Timeout in ms for act operations (deprecated, v2 only) */ fun actTimeoutMs(actTimeoutMs: Double) = apply { body.actTimeoutMs(actTimeoutMs) } /** @@ -376,17 +365,6 @@ private constructor( body.browserbaseSessionId(browserbaseSessionId) } - fun debugDom(debugDom: Boolean) = apply { body.debugDom(debugDom) } - - /** - * Sets [Builder.debugDom] to an arbitrary JSON value. - * - * You should usually call [Builder.debugDom] with a well-typed [Boolean] value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. - */ - fun debugDom(debugDom: JsonField) = apply { body.debugDom(debugDom) } - /** Timeout in ms to wait for DOM to settle */ fun domSettleTimeoutMs(domSettleTimeoutMs: Double) = apply { body.domSettleTimeoutMs(domSettleTimeoutMs) @@ -453,6 +431,7 @@ private constructor( */ fun verbose(verbose: JsonField) = apply { body.verbose(verbose) } + /** Wait for captcha solves (deprecated, v2 only) */ fun waitForCaptchaSolves(waitForCaptchaSolves: Boolean) = apply { body.waitForCaptchaSolves(waitForCaptchaSolves) } @@ -632,7 +611,6 @@ private constructor( private val browser: JsonField, private val browserbaseSessionCreateParams: JsonField, private val browserbaseSessionId: JsonField, - private val debugDom: JsonField, private val domSettleTimeoutMs: JsonField, private val experimental: JsonField, private val selfHeal: JsonField, @@ -658,9 +636,6 @@ private constructor( @JsonProperty("browserbaseSessionID") @ExcludeMissing browserbaseSessionId: JsonField = JsonMissing.of(), - @JsonProperty("debugDom") - @ExcludeMissing - debugDom: JsonField = JsonMissing.of(), @JsonProperty("domSettleTimeoutMs") @ExcludeMissing domSettleTimeoutMs: JsonField = JsonMissing.of(), @@ -683,7 +658,6 @@ private constructor( browser, browserbaseSessionCreateParams, browserbaseSessionId, - debugDom, domSettleTimeoutMs, experimental, selfHeal, @@ -702,7 +676,7 @@ private constructor( fun modelName(): String = modelName.getRequired("modelName") /** - * Timeout in ms for act operations + * Timeout in ms for act operations (deprecated, v2 only) * * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if * the server responded with an unexpected value). @@ -731,12 +705,6 @@ private constructor( fun browserbaseSessionId(): String? = browserbaseSessionId.getNullable("browserbaseSessionID") - /** - * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if - * the server responded with an unexpected value). - */ - fun debugDom(): Boolean? = debugDom.getNullable("debugDom") - /** * Timeout in ms to wait for DOM to settle * @@ -776,6 +744,8 @@ private constructor( fun verbose(): Verbose? = verbose.getNullable("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). */ @@ -827,13 +797,6 @@ private constructor( @ExcludeMissing fun _browserbaseSessionId(): JsonField = browserbaseSessionId - /** - * Returns the raw JSON value of [debugDom]. - * - * Unlike [debugDom], this method doesn't throw if the JSON field has an unexpected type. - */ - @JsonProperty("debugDom") @ExcludeMissing fun _debugDom(): JsonField = debugDom - /** * Returns the raw JSON value of [domSettleTimeoutMs]. * @@ -922,7 +885,6 @@ private constructor( private var browserbaseSessionCreateParams: JsonField = JsonMissing.of() private var browserbaseSessionId: JsonField = JsonMissing.of() - private var debugDom: JsonField = JsonMissing.of() private var domSettleTimeoutMs: JsonField = JsonMissing.of() private var experimental: JsonField = JsonMissing.of() private var selfHeal: JsonField = JsonMissing.of() @@ -937,7 +899,6 @@ private constructor( browser = body.browser browserbaseSessionCreateParams = body.browserbaseSessionCreateParams browserbaseSessionId = body.browserbaseSessionId - debugDom = body.debugDom domSettleTimeoutMs = body.domSettleTimeoutMs experimental = body.experimental selfHeal = body.selfHeal @@ -959,7 +920,7 @@ private constructor( */ fun modelName(modelName: JsonField) = apply { this.modelName = modelName } - /** Timeout in ms for act operations */ + /** Timeout in ms for act operations (deprecated, v2 only) */ fun actTimeoutMs(actTimeoutMs: Double) = actTimeoutMs(JsonField.of(actTimeoutMs)) /** @@ -1014,17 +975,6 @@ private constructor( this.browserbaseSessionId = browserbaseSessionId } - fun debugDom(debugDom: Boolean) = debugDom(JsonField.of(debugDom)) - - /** - * Sets [Builder.debugDom] to an arbitrary JSON value. - * - * You should usually call [Builder.debugDom] with a well-typed [Boolean] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. - */ - fun debugDom(debugDom: JsonField) = apply { this.debugDom = debugDom } - /** Timeout in ms to wait for DOM to settle */ fun domSettleTimeoutMs(domSettleTimeoutMs: Double) = domSettleTimeoutMs(JsonField.of(domSettleTimeoutMs)) @@ -1091,6 +1041,7 @@ private constructor( */ fun verbose(verbose: JsonField) = apply { this.verbose = verbose } + /** Wait for captcha solves (deprecated, v2 only) */ fun waitForCaptchaSolves(waitForCaptchaSolves: Boolean) = waitForCaptchaSolves(JsonField.of(waitForCaptchaSolves)) @@ -1143,7 +1094,6 @@ private constructor( browser, browserbaseSessionCreateParams, browserbaseSessionId, - debugDom, domSettleTimeoutMs, experimental, selfHeal, @@ -1166,7 +1116,6 @@ private constructor( browser()?.validate() browserbaseSessionCreateParams()?.validate() browserbaseSessionId() - debugDom() domSettleTimeoutMs() experimental() selfHeal() @@ -1196,7 +1145,6 @@ private constructor( (browser.asKnown()?.validity() ?: 0) + (browserbaseSessionCreateParams.asKnown()?.validity() ?: 0) + (if (browserbaseSessionId.asKnown() == null) 0 else 1) + - (if (debugDom.asKnown() == null) 0 else 1) + (if (domSettleTimeoutMs.asKnown() == null) 0 else 1) + (if (experimental.asKnown() == null) 0 else 1) + (if (selfHeal.asKnown() == null) 0 else 1) + @@ -1215,7 +1163,6 @@ private constructor( browser == other.browser && browserbaseSessionCreateParams == other.browserbaseSessionCreateParams && browserbaseSessionId == other.browserbaseSessionId && - debugDom == other.debugDom && domSettleTimeoutMs == other.domSettleTimeoutMs && experimental == other.experimental && selfHeal == other.selfHeal && @@ -1232,7 +1179,6 @@ private constructor( browser, browserbaseSessionCreateParams, browserbaseSessionId, - debugDom, domSettleTimeoutMs, experimental, selfHeal, @@ -1246,7 +1192,7 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "Body{modelName=$modelName, actTimeoutMs=$actTimeoutMs, browser=$browser, browserbaseSessionCreateParams=$browserbaseSessionCreateParams, browserbaseSessionId=$browserbaseSessionId, debugDom=$debugDom, domSettleTimeoutMs=$domSettleTimeoutMs, experimental=$experimental, selfHeal=$selfHeal, systemPrompt=$systemPrompt, verbose=$verbose, waitForCaptchaSolves=$waitForCaptchaSolves, additionalProperties=$additionalProperties}" + "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}" } class Browser @@ -3373,13 +3319,9 @@ private constructor( /** Alias for calling [proxies] with `Proxies.ofBoolean(boolean)`. */ fun proxies(boolean: Boolean) = proxies(Proxies.ofBoolean(boolean)) - /** - * Alias for calling [proxies] with - * `Proxies.ofUnnamedSchemaWithArrayParent0s(unnamedSchemaWithArrayParent0s)`. - */ - fun proxiesOfUnnamedSchemaWithArrayParent0s( - unnamedSchemaWithArrayParent0s: List - ) = proxies(Proxies.ofUnnamedSchemaWithArrayParent0s(unnamedSchemaWithArrayParent0s)) + /** Alias for calling [proxies] with `Proxies.ofProxyConfigList(proxyConfigList)`. */ + fun proxiesOfProxyConfigList(proxyConfigList: List) = + proxies(Proxies.ofProxyConfigList(proxyConfigList)) fun region(region: Region) = region(JsonField.of(region)) @@ -5611,31 +5553,29 @@ private constructor( class Proxies private constructor( private val boolean: Boolean? = null, - private val unnamedSchemaWithArrayParent0s: List? = null, + private val proxyConfigList: List? = null, private val _json: JsonValue? = null, ) { fun boolean(): Boolean? = boolean - fun unnamedSchemaWithArrayParent0s(): List? = - unnamedSchemaWithArrayParent0s + fun proxyConfigList(): List? = proxyConfigList fun isBoolean(): Boolean = boolean != null - fun isUnnamedSchemaWithArrayParent0s(): Boolean = unnamedSchemaWithArrayParent0s != null + fun isProxyConfigList(): Boolean = proxyConfigList != null fun asBoolean(): Boolean = boolean.getOrThrow("boolean") - fun asUnnamedSchemaWithArrayParent0s(): List = - unnamedSchemaWithArrayParent0s.getOrThrow("unnamedSchemaWithArrayParent0s") + fun asProxyConfigList(): List = + proxyConfigList.getOrThrow("proxyConfigList") fun _json(): JsonValue? = _json fun accept(visitor: Visitor): T = when { boolean != null -> visitor.visitBoolean(boolean) - unnamedSchemaWithArrayParent0s != null -> - visitor.visitUnnamedSchemaWithArrayParent0s(unnamedSchemaWithArrayParent0s) + proxyConfigList != null -> visitor.visitProxyConfigList(proxyConfigList) else -> visitor.unknown(_json) } @@ -5650,10 +5590,8 @@ private constructor( object : Visitor { override fun visitBoolean(boolean: Boolean) {} - override fun visitUnnamedSchemaWithArrayParent0s( - unnamedSchemaWithArrayParent0s: List - ) { - unnamedSchemaWithArrayParent0s.forEach { it.validate() } + override fun visitProxyConfigList(proxyConfigList: List) { + proxyConfigList.forEach { it.validate() } } } ) @@ -5679,9 +5617,8 @@ private constructor( object : Visitor { override fun visitBoolean(boolean: Boolean) = 1 - override fun visitUnnamedSchemaWithArrayParent0s( - unnamedSchemaWithArrayParent0s: List - ) = unnamedSchemaWithArrayParent0s.sumOf { it.validity().toInt() } + override fun visitProxyConfigList(proxyConfigList: List) = + proxyConfigList.sumOf { it.validity().toInt() } override fun unknown(json: JsonValue?) = 0 } @@ -5694,16 +5631,15 @@ private constructor( return other is Proxies && boolean == other.boolean && - unnamedSchemaWithArrayParent0s == other.unnamedSchemaWithArrayParent0s + proxyConfigList == other.proxyConfigList } - override fun hashCode(): Int = Objects.hash(boolean, unnamedSchemaWithArrayParent0s) + override fun hashCode(): Int = Objects.hash(boolean, proxyConfigList) override fun toString(): String = when { boolean != null -> "Proxies{boolean=$boolean}" - unnamedSchemaWithArrayParent0s != null -> - "Proxies{unnamedSchemaWithArrayParent0s=$unnamedSchemaWithArrayParent0s}" + proxyConfigList != null -> "Proxies{proxyConfigList=$proxyConfigList}" _json != null -> "Proxies{_unknown=$_json}" else -> throw IllegalStateException("Invalid Proxies") } @@ -5712,13 +5648,8 @@ private constructor( fun ofBoolean(boolean: Boolean) = Proxies(boolean = boolean) - fun ofUnnamedSchemaWithArrayParent0s( - unnamedSchemaWithArrayParent0s: List - ) = - Proxies( - unnamedSchemaWithArrayParent0s = - unnamedSchemaWithArrayParent0s.toImmutable() - ) + fun ofProxyConfigList(proxyConfigList: List) = + Proxies(proxyConfigList = proxyConfigList.toImmutable()) } /** @@ -5729,9 +5660,7 @@ private constructor( fun visitBoolean(boolean: Boolean): T - fun visitUnnamedSchemaWithArrayParent0s( - unnamedSchemaWithArrayParent0s: List - ): T + fun visitProxyConfigList(proxyConfigList: List): T /** * Maps an unknown variant of [Proxies] to a value of type [T]. @@ -5758,13 +5687,9 @@ private constructor( tryDeserialize(node, jacksonTypeRef())?.let { Proxies(boolean = it, _json = json) }, - tryDeserialize( - node, - jacksonTypeRef>(), - ) - ?.let { - Proxies(unnamedSchemaWithArrayParent0s = it, _json = json) - }, + tryDeserialize(node, jacksonTypeRef>())?.let { + Proxies(proxyConfigList = it, _json = json) + }, ) .filterNotNull() .allMaxBy { it.validity() } @@ -5791,67 +5716,59 @@ private constructor( ) { when { value.boolean != null -> generator.writeObject(value.boolean) - value.unnamedSchemaWithArrayParent0s != null -> - generator.writeObject(value.unnamedSchemaWithArrayParent0s) + value.proxyConfigList != null -> + generator.writeObject(value.proxyConfigList) value._json != null -> generator.writeObject(value._json) else -> throw IllegalStateException("Invalid Proxies") } } } - @JsonDeserialize(using = UnnamedSchemaWithArrayParent0.Deserializer::class) - @JsonSerialize(using = UnnamedSchemaWithArrayParent0.Serializer::class) - class UnnamedSchemaWithArrayParent0 + @JsonDeserialize(using = ProxyConfig.Deserializer::class) + @JsonSerialize(using = ProxyConfig.Serializer::class) + class ProxyConfig private constructor( - private val browserbaseProxyConfig: BrowserbaseProxyConfig? = null, - private val externalProxyConfig: ExternalProxyConfig? = null, + private val browserbase: Browserbase? = null, + private val external: External? = null, private val _json: JsonValue? = null, ) { - fun browserbaseProxyConfig(): BrowserbaseProxyConfig? = browserbaseProxyConfig + fun browserbase(): Browserbase? = browserbase - fun externalProxyConfig(): ExternalProxyConfig? = externalProxyConfig + fun external(): External? = external - fun isBrowserbaseProxyConfig(): Boolean = browserbaseProxyConfig != null + fun isBrowserbase(): Boolean = browserbase != null - fun isExternalProxyConfig(): Boolean = externalProxyConfig != null + fun isExternal(): Boolean = external != null - fun asBrowserbaseProxyConfig(): BrowserbaseProxyConfig = - browserbaseProxyConfig.getOrThrow("browserbaseProxyConfig") + fun asBrowserbase(): Browserbase = browserbase.getOrThrow("browserbase") - fun asExternalProxyConfig(): ExternalProxyConfig = - externalProxyConfig.getOrThrow("externalProxyConfig") + fun asExternal(): External = external.getOrThrow("external") fun _json(): JsonValue? = _json fun accept(visitor: Visitor): T = when { - browserbaseProxyConfig != null -> - visitor.visitBrowserbaseProxyConfig(browserbaseProxyConfig) - externalProxyConfig != null -> - visitor.visitExternalProxyConfig(externalProxyConfig) + browserbase != null -> visitor.visitBrowserbase(browserbase) + external != null -> visitor.visitExternal(external) else -> visitor.unknown(_json) } private var validated: Boolean = false - fun validate(): UnnamedSchemaWithArrayParent0 = apply { + fun validate(): ProxyConfig = apply { if (validated) { return@apply } accept( object : Visitor { - override fun visitBrowserbaseProxyConfig( - browserbaseProxyConfig: BrowserbaseProxyConfig - ) { - browserbaseProxyConfig.validate() + override fun visitBrowserbase(browserbase: Browserbase) { + browserbase.validate() } - override fun visitExternalProxyConfig( - externalProxyConfig: ExternalProxyConfig - ) { - externalProxyConfig.validate() + override fun visitExternal(external: External) { + external.validate() } } ) @@ -5875,13 +5792,10 @@ private constructor( internal fun validity(): Int = accept( object : Visitor { - override fun visitBrowserbaseProxyConfig( - browserbaseProxyConfig: BrowserbaseProxyConfig - ) = browserbaseProxyConfig.validity() + override fun visitBrowserbase(browserbase: Browserbase) = + browserbase.validity() - override fun visitExternalProxyConfig( - externalProxyConfig: ExternalProxyConfig - ) = externalProxyConfig.validity() + override fun visitExternal(external: External) = external.validity() override fun unknown(json: JsonValue?) = 0 } @@ -5892,135 +5806,97 @@ private constructor( return true } - return other is UnnamedSchemaWithArrayParent0 && - browserbaseProxyConfig == other.browserbaseProxyConfig && - externalProxyConfig == other.externalProxyConfig + return other is ProxyConfig && + browserbase == other.browserbase && + external == other.external } - override fun hashCode(): Int = - Objects.hash(browserbaseProxyConfig, externalProxyConfig) + override fun hashCode(): Int = Objects.hash(browserbase, external) override fun toString(): String = when { - browserbaseProxyConfig != null -> - "UnnamedSchemaWithArrayParent0{browserbaseProxyConfig=$browserbaseProxyConfig}" - externalProxyConfig != null -> - "UnnamedSchemaWithArrayParent0{externalProxyConfig=$externalProxyConfig}" - _json != null -> "UnnamedSchemaWithArrayParent0{_unknown=$_json}" - else -> throw IllegalStateException("Invalid UnnamedSchemaWithArrayParent0") + browserbase != null -> "ProxyConfig{browserbase=$browserbase}" + external != null -> "ProxyConfig{external=$external}" + _json != null -> "ProxyConfig{_unknown=$_json}" + else -> throw IllegalStateException("Invalid ProxyConfig") } companion object { - fun ofBrowserbaseProxyConfig(browserbaseProxyConfig: BrowserbaseProxyConfig) = - UnnamedSchemaWithArrayParent0( - browserbaseProxyConfig = browserbaseProxyConfig - ) + fun ofBrowserbase(browserbase: Browserbase) = + ProxyConfig(browserbase = browserbase) - fun ofExternalProxyConfig(externalProxyConfig: ExternalProxyConfig) = - UnnamedSchemaWithArrayParent0(externalProxyConfig = externalProxyConfig) + fun ofExternal(external: External) = ProxyConfig(external = external) } /** - * An interface that defines how to map each variant of - * [UnnamedSchemaWithArrayParent0] to a value of type [T]. + * An interface that defines how to map each variant of [ProxyConfig] to a value of + * type [T]. */ interface Visitor { - fun visitBrowserbaseProxyConfig( - browserbaseProxyConfig: BrowserbaseProxyConfig - ): T + fun visitBrowserbase(browserbase: Browserbase): T - fun visitExternalProxyConfig(externalProxyConfig: ExternalProxyConfig): T + fun visitExternal(external: External): T /** - * Maps an unknown variant of [UnnamedSchemaWithArrayParent0] to a value of type - * [T]. + * Maps an unknown variant of [ProxyConfig] to a value of type [T]. * - * An instance of [UnnamedSchemaWithArrayParent0] 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. + * 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 UnnamedSchemaWithArrayParent0: $json" - ) + throw StagehandInvalidDataException("Unknown ProxyConfig: $json") } } - internal class Deserializer : - BaseDeserializer( - UnnamedSchemaWithArrayParent0::class - ) { + internal class Deserializer : BaseDeserializer(ProxyConfig::class) { - override fun ObjectCodec.deserialize( - node: JsonNode - ): UnnamedSchemaWithArrayParent0 { + override fun ObjectCodec.deserialize(node: JsonNode): ProxyConfig { val json = JsonValue.fromJsonNode(node) + val type = json.asObject()?.get("type")?.asString() - val bestMatches = - sequenceOf( - tryDeserialize(node, jacksonTypeRef()) - ?.let { - UnnamedSchemaWithArrayParent0( - browserbaseProxyConfig = it, - _json = json, - ) - }, - tryDeserialize(node, jacksonTypeRef()) - ?.let { - UnnamedSchemaWithArrayParent0( - externalProxyConfig = 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 -> UnnamedSchemaWithArrayParent0(_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() + 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( - UnnamedSchemaWithArrayParent0::class - ) { + internal class Serializer : BaseSerializer(ProxyConfig::class) { override fun serialize( - value: UnnamedSchemaWithArrayParent0, + value: ProxyConfig, generator: JsonGenerator, provider: SerializerProvider, ) { when { - value.browserbaseProxyConfig != null -> - generator.writeObject(value.browserbaseProxyConfig) - value.externalProxyConfig != null -> - generator.writeObject(value.externalProxyConfig) + 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 UnnamedSchemaWithArrayParent0") + else -> throw IllegalStateException("Invalid ProxyConfig") } } } - class BrowserbaseProxyConfig + class Browserbase @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val type: JsonField, + private val type: JsonValue, private val domainPattern: JsonField, private val geolocation: JsonField, private val additionalProperties: MutableMap, @@ -6028,9 +5904,7 @@ private constructor( @JsonCreator private constructor( - @JsonProperty("type") - @ExcludeMissing - type: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("domainPattern") @ExcludeMissing domainPattern: JsonField = JsonMissing.of(), @@ -6040,11 +5914,15 @@ private constructor( ) : this(type, domainPattern, geolocation, 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). + * Expected to always return the following: + * ```kotlin + * JsonValue.from("browserbase") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the + * server responded with an unexpected value). */ - fun type(): Type = type.getRequired("type") + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** * @throws StagehandInvalidDataException if the JSON field has an unexpected @@ -6058,14 +5936,6 @@ private constructor( */ fun geolocation(): Geolocation? = geolocation.getNullable("geolocation") - /** - * 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 [domainPattern]. * @@ -6101,44 +5971,40 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of - * [BrowserbaseProxyConfig]. - * - * The following fields are required: - * ```kotlin - * .type() - * ``` + * Returns a mutable builder for constructing an instance of [Browserbase]. */ fun builder() = Builder() } - /** A builder for [BrowserbaseProxyConfig]. */ + /** A builder for [Browserbase]. */ class Builder internal constructor() { - private var type: JsonField? = null + private var type: JsonValue = JsonValue.from("browserbase") private var domainPattern: JsonField = JsonMissing.of() private var geolocation: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() - internal fun from(browserbaseProxyConfig: BrowserbaseProxyConfig) = apply { - type = browserbaseProxyConfig.type - domainPattern = browserbaseProxyConfig.domainPattern - geolocation = browserbaseProxyConfig.geolocation - additionalProperties = - browserbaseProxyConfig.additionalProperties.toMutableMap() + internal fun from(browserbase: Browserbase) = apply { + type = browserbase.type + domainPattern = browserbase.domainPattern + geolocation = browserbase.geolocation + additionalProperties = browserbase.additionalProperties.toMutableMap() } - fun type(type: Type) = type(JsonField.of(type)) - /** - * Sets [Builder.type] to an arbitrary JSON value. + * Sets the field 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. + * It is usually unnecessary to call this method because the field defaults + * to the following: + * ```kotlin + * JsonValue.from("browserbase") + * ``` + * + * 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 type(type: JsonValue) = apply { this.type = type } fun domainPattern(domainPattern: String) = domainPattern(JsonField.of(domainPattern)) @@ -6191,20 +6057,13 @@ private constructor( } /** - * Returns an immutable instance of [BrowserbaseProxyConfig]. + * Returns an immutable instance of [Browserbase]. * * Further updates to this [Builder] will not mutate the returned instance. - * - * The following fields are required: - * ```kotlin - * .type() - * ``` - * - * @throws IllegalStateException if any required field is unset. */ - fun build(): BrowserbaseProxyConfig = - BrowserbaseProxyConfig( - checkRequired("type", type), + fun build(): Browserbase = + Browserbase( + type, domainPattern, geolocation, additionalProperties.toMutableMap(), @@ -6213,12 +6072,18 @@ private constructor( private var validated: Boolean = false - fun validate(): BrowserbaseProxyConfig = apply { + fun validate(): Browserbase = apply { if (validated) { return@apply } - type().validate() + _type().let { + if (it != JsonValue.from("browserbase")) { + throw StagehandInvalidDataException( + "'type' is invalid, received $it" + ) + } + } domainPattern() geolocation()?.validate() validated = true @@ -6239,137 +6104,10 @@ private constructor( * Used for best match union deserialization. */ internal fun validity(): Int = - (type.asKnown()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("browserbase")) 1 else 0 } + (if (domainPattern.asKnown() == null) 0 else 1) + (geolocation.asKnown()?.validity() ?: 0) - class Type - @JsonCreator - private constructor(private val value: JsonField) : Enum { - - /** - * Returns this class instance's raw value. - * - * This is usually only useful if this instance was deserialized from data - * that doesn't match any known member, and you want to know that value. For - * example, if the SDK is on an older version than the API, then the API may - * respond with new members that the SDK is unaware of. - */ - @com.fasterxml.jackson.annotation.JsonValue - fun _value(): JsonField = value - - companion object { - - val BROWSERBASE = of("browserbase") - - fun of(value: String) = Type(JsonField.of(value)) - } - - /** An enum containing [Type]'s known values. */ - enum class Known { - 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 { - 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) { - 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) { - 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() - ?: throw 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. - */ - 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() - } - class Geolocation @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( @@ -6620,7 +6358,7 @@ private constructor( return true } - return other is BrowserbaseProxyConfig && + return other is Browserbase && type == other.type && domainPattern == other.domainPattern && geolocation == other.geolocation && @@ -6634,14 +6372,14 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "BrowserbaseProxyConfig{type=$type, domainPattern=$domainPattern, geolocation=$geolocation, additionalProperties=$additionalProperties}" + "Browserbase{type=$type, domainPattern=$domainPattern, geolocation=$geolocation, additionalProperties=$additionalProperties}" } - class ExternalProxyConfig + class External @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val server: JsonField, - private val type: JsonField, + private val type: JsonValue, private val domainPattern: JsonField, private val password: JsonField, private val username: JsonField, @@ -6653,9 +6391,7 @@ private constructor( @JsonProperty("server") @ExcludeMissing server: JsonField = JsonMissing.of(), - @JsonProperty("type") - @ExcludeMissing - type: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("domainPattern") @ExcludeMissing domainPattern: JsonField = JsonMissing.of(), @@ -6675,11 +6411,15 @@ private constructor( fun server(): String = server.getRequired("server") /** - * @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). + * Expected to always return the following: + * ```kotlin + * JsonValue.from("external") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the + * server responded with an unexpected value). */ - fun type(): Type = type.getRequired("type") + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type /** * @throws StagehandInvalidDataException if the JSON field has an unexpected @@ -6709,14 +6449,6 @@ private constructor( @ExcludeMissing fun _server(): JsonField = server - /** - * 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 [domainPattern]. * @@ -6762,37 +6494,34 @@ private constructor( companion object { /** - * Returns a mutable builder for constructing an instance of - * [ExternalProxyConfig]. + * Returns a mutable builder for constructing an instance of [External]. * * The following fields are required: * ```kotlin * .server() - * .type() * ``` */ fun builder() = Builder() } - /** A builder for [ExternalProxyConfig]. */ + /** A builder for [External]. */ class Builder internal constructor() { private var server: JsonField? = null - private var type: 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() - internal fun from(externalProxyConfig: ExternalProxyConfig) = apply { - server = externalProxyConfig.server - type = externalProxyConfig.type - domainPattern = externalProxyConfig.domainPattern - password = externalProxyConfig.password - username = externalProxyConfig.username - additionalProperties = - externalProxyConfig.additionalProperties.toMutableMap() + 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)) @@ -6806,16 +6535,19 @@ private constructor( */ fun server(server: JsonField) = apply { this.server = server } - fun type(type: Type) = type(JsonField.of(type)) - /** - * Sets [Builder.type] to an arbitrary JSON value. + * Sets the field 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. + * It is usually unnecessary to call this method because the field defaults + * to the following: + * ```kotlin + * JsonValue.from("external") + * ``` + * + * 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 type(type: JsonValue) = apply { this.type = type } fun domainPattern(domainPattern: String) = domainPattern(JsonField.of(domainPattern)) @@ -6880,22 +6612,21 @@ private constructor( } /** - * Returns an immutable instance of [ExternalProxyConfig]. + * Returns an immutable instance of [External]. * * Further updates to this [Builder] will not mutate the returned instance. * * The following fields are required: * ```kotlin * .server() - * .type() * ``` * * @throws IllegalStateException if any required field is unset. */ - fun build(): ExternalProxyConfig = - ExternalProxyConfig( + fun build(): External = + External( checkRequired("server", server), - checkRequired("type", type), + type, domainPattern, password, username, @@ -6905,13 +6636,19 @@ private constructor( private var validated: Boolean = false - fun validate(): ExternalProxyConfig = apply { + fun validate(): External = apply { if (validated) { return@apply } server() - type().validate() + _type().let { + if (it != JsonValue.from("external")) { + throw StagehandInvalidDataException( + "'type' is invalid, received $it" + ) + } + } domainPattern() password() username() @@ -6934,144 +6671,17 @@ private constructor( */ internal fun validity(): Int = (if (server.asKnown() == null) 0 else 1) + - (type.asKnown()?.validity() ?: 0) + + type.let { if (it == JsonValue.from("external")) 1 else 0 } + (if (domainPattern.asKnown() == null) 0 else 1) + (if (password.asKnown() == null) 0 else 1) + (if (username.asKnown() == null) 0 else 1) - 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 { - - val EXTERNAL = of("external") - - fun of(value: String) = Type(JsonField.of(value)) - } - - /** An enum containing [Type]'s known values. */ - enum class Known { - EXTERNAL - } - - /** - * 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 { - EXTERNAL, - /** - * 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) { - EXTERNAL -> Value.EXTERNAL - 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) { - EXTERNAL -> Known.EXTERNAL - 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() - ?: throw 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. - */ - 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 ExternalProxyConfig && + return other is External && server == other.server && type == other.type && domainPattern == other.domainPattern && @@ -7094,7 +6704,7 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "ExternalProxyConfig{server=$server, type=$type, domainPattern=$domainPattern, password=$password, username=$username, additionalProperties=$additionalProperties}" + "External{server=$server, type=$type, domainPattern=$domainPattern, password=$password, username=$username, additionalProperties=$additionalProperties}" } } } @@ -7376,7 +6986,7 @@ private constructor( } /** Logging verbosity level (0=quiet, 1=normal, 2=debug) */ - class Verbose @JsonCreator private constructor(private val value: JsonField) : Enum { + class Verbose @JsonCreator private constructor(private val value: JsonField) : Enum { /** * Returns this class instance's raw value. @@ -7386,17 +6996,17 @@ private constructor( * 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 + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value companion object { - val _0 = of(0.0) + val _0 = of("0") - val _1 = of(1.0) + val _1 = of("1") - val _2 = of(2.0) + val _2 = of("2") - fun of(value: Double) = Verbose(JsonField.of(value)) + fun of(value: String) = Verbose(JsonField.of(value)) } /** An enum containing [Verbose]'s known values. */ @@ -7458,12 +7068,14 @@ private constructor( /** * 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 asDouble(): Double = - _value().asNumber()?.toDouble() - ?: throw StagehandInvalidDataException("Value is not a Double") + fun asString(): String = + _value().asString() ?: throw StagehandInvalidDataException("Value is not a String") private var validated: Boolean = false diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartResponse.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartResponse.kt index f69ed30..c6bb201 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartResponse.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionStartResponse.kt @@ -2,7 +2,6 @@ package com.browserbase.api.models.sessions -import com.browserbase.api.core.Enum import com.browserbase.api.core.ExcludeMissing import com.browserbase.api.core.JsonField import com.browserbase.api.core.JsonMissing @@ -20,14 +19,14 @@ class SessionStartResponse @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val data: JsonField, - private val success: 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(), + @JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(), ) : this(data, success, mutableMapOf()) /** @@ -37,10 +36,12 @@ private constructor( 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(): Success = success.getRequired("success") + fun success(): Boolean = success.getRequired("success") /** * Returns the raw JSON value of [data]. @@ -54,7 +55,7 @@ private constructor( * * Unlike [success], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success + @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -86,7 +87,7 @@ private constructor( class Builder internal constructor() { private var data: JsonField? = null - private var success: JsonField? = null + private var success: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() internal fun from(sessionStartResponse: SessionStartResponse) = apply { @@ -105,15 +106,16 @@ private constructor( */ fun data(data: JsonField) = apply { this.data = data } - fun success(success: Success) = success(JsonField.of(success)) + /** 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 [Success] 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 success(success: JsonField) = apply { this.success = success } + fun success(success: JsonField) = apply { this.success = success } fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -163,7 +165,7 @@ private constructor( } data().validate() - success().validate() + success() validated = true } @@ -181,13 +183,14 @@ private constructor( * Used for best match union deserialization. */ internal fun validity(): Int = - (data.asKnown()?.validity() ?: 0) + (success.asKnown()?.validity() ?: 0) + (data.asKnown()?.validity() ?: 0) + (if (success.asKnown() == null) 0 else 1) 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, ) { @@ -199,7 +202,8 @@ private constructor( @JsonProperty("sessionId") @ExcludeMissing sessionId: JsonField = JsonMissing.of(), - ) : this(available, sessionId, mutableMapOf()) + @JsonProperty("cdpUrl") @ExcludeMissing cdpUrl: JsonField = JsonMissing.of(), + ) : this(available, sessionId, cdpUrl, mutableMapOf()) /** * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is @@ -208,13 +212,22 @@ private constructor( fun available(): Boolean = available.getRequired("available") /** - * Unique session identifier + * 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(): String? = cdpUrl.getNullable("cdpUrl") + /** * Returns the raw JSON value of [available]. * @@ -229,6 +242,13 @@ private constructor( */ @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) @@ -260,11 +280,13 @@ private constructor( private var available: JsonField? = null private var sessionId: JsonField? = null + private var cdpUrl: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() internal fun from(data: Data) = apply { available = data.available sessionId = data.sessionId + cdpUrl = data.cdpUrl additionalProperties = data.additionalProperties.toMutableMap() } @@ -279,7 +301,7 @@ private constructor( */ fun available(available: JsonField) = apply { this.available = available } - /** Unique session identifier */ + /** Unique Browserbase session identifier */ fun sessionId(sessionId: String) = sessionId(JsonField.of(sessionId)) /** @@ -291,6 +313,21 @@ private constructor( */ 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)) + + /** + * 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) @@ -327,6 +364,7 @@ private constructor( Data( checkRequired("available", available), checkRequired("sessionId", sessionId), + cdpUrl, additionalProperties.toMutableMap(), ) } @@ -340,6 +378,7 @@ private constructor( available() sessionId() + cdpUrl() validated = true } @@ -359,7 +398,8 @@ private constructor( */ internal fun validity(): Int = (if (available.asKnown() == null) 0 else 1) + - (if (sessionId.asKnown() == null) 0 else 1) + (if (sessionId.asKnown() == null) 0 else 1) + + (if (cdpUrl.asKnown() == null) 0 else 1) override fun equals(other: Any?): Boolean { if (this === other) { @@ -369,133 +409,18 @@ private constructor( 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, additionalProperties) + Objects.hash(available, sessionId, cdpUrl, additionalProperties) } override fun hashCode(): Int = hashCode override fun toString() = - "Data{available=$available, sessionId=$sessionId, additionalProperties=$additionalProperties}" - } - - class Success @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 { - - val TRUE = of(true) - - fun of(value: Boolean) = Success(JsonField.of(value)) - } - - /** An enum containing [Success]'s known values. */ - enum class Known { - TRUE - } - - /** - * An enum containing [Success]'s known values, as well as an [_UNKNOWN] member. - * - * An instance of [Success] 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, - /** An enum member indicating that [Success] 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 - 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 - else -> throw StagehandInvalidDataException("Unknown Success: $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 asBoolean(): Boolean = - _value().asBoolean() ?: throw StagehandInvalidDataException("Value is not a Boolean") - - private var validated: Boolean = false - - fun validate(): Success = 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. - */ - 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 Success && value == other.value - } - - override fun hashCode() = value.hashCode() - - override fun toString() = value.toString() + "Data{available=$available, sessionId=$sessionId, cdpUrl=$cdpUrl, additionalProperties=$additionalProperties}" } override fun equals(other: Any?): Boolean { diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/StreamEvent.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/StreamEvent.kt new file mode 100644 index 0000000..a385421 --- /dev/null +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/models/sessions/StreamEvent.kt @@ -0,0 +1,1162 @@ +// 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 + +/** + * 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: + * ```kotlin + * .id() + * .data() + * .type() + * ``` + */ + 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() + + 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: + * ```kotlin + * .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. + */ + internal fun validity(): Int = + (if (id.asKnown() == null) 0 else 1) + + (data.asKnown()?.validity() ?: 0) + + (type.asKnown()?.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(): StreamEventSystemDataOutput? = + streamEventSystemDataOutput + + fun streamEventLogDataOutput(): StreamEventLogDataOutput? = 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(): JsonValue? = _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. + */ + 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 { + + fun ofStreamEventSystemDataOutput( + streamEventSystemDataOutput: StreamEventSystemDataOutput + ) = Data(streamEventSystemDataOutput = streamEventSystemDataOutput) + + 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(): String? = error.getNullable("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: + * ```kotlin + * .status() + * ``` + */ + 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() + + 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: + * ```kotlin + * .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. + */ + internal fun validity(): Int = + (status.asKnown()?.validity() ?: 0) + (if (error.asKnown() == null) 0 else 1) + + /** 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 { + + val STARTING = of("starting") + + val CONNECTED = of("connected") + + val RUNNING = of("running") + + val FINISHED = of("finished") + + val ERROR = of("error") + + 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() + ?: throw 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. + */ + 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: + * ```kotlin + * 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: + * ```kotlin + * .message() + * ``` + */ + 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() + + 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: + * ```kotlin + * 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: + * ```kotlin + * .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. + */ + internal fun validity(): Int = + (if (message.asKnown() == null) 0 else 1) + + 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 { + + val SYSTEM = of("system") + + val LOG = of("log") + + 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() ?: throw 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. + */ + 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-kotlin-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionService.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionService.kt index e40472d..70cfd19 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionService.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionService.kt @@ -5,6 +5,7 @@ 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.core.http.StreamResponse import com.browserbase.api.models.sessions.SessionActParams import com.browserbase.api.models.sessions.SessionActResponse import com.browserbase.api.models.sessions.SessionEndParams @@ -19,6 +20,7 @@ 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 interface SessionService { @@ -50,6 +52,23 @@ interface SessionService { requestOptions: RequestOptions = RequestOptions.none(), ): SessionActResponse + /** + * Executes a browser action using natural language instructions or a predefined Action object. + */ + @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, + requestOptions: RequestOptions = RequestOptions.none(), + ): StreamResponse + /** Terminates the browser session and releases all associated resources. */ fun end( id: String, @@ -80,6 +99,22 @@ interface SessionService { 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, + requestOptions: RequestOptions = RequestOptions.none(), + ): StreamResponse = + executeStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see executeStreaming */ + @MustBeClosed + fun executeStreaming( + params: SessionExecuteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): StreamResponse + /** Extracts structured data from the current page using AI-powered analysis. */ fun extract( id: String, @@ -97,6 +132,27 @@ interface SessionService { 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, + params: SessionExtractParams = SessionExtractParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): StreamResponse = + extractStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + params: SessionExtractParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): StreamResponse + + /** @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, @@ -130,6 +186,30 @@ interface SessionService { fun observe(id: String, requestOptions: RequestOptions): SessionObserveResponse = observe(id, SessionObserveParams.none(), requestOptions) + /** + * Identifies and returns available actions on the current page that match the given + * instruction. + */ + @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( + params: SessionObserveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): StreamResponse + + /** @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. @@ -168,6 +248,25 @@ interface SessionService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** + * 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, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> = + actStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @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]. @@ -211,6 +310,25 @@ interface SessionService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** + * Returns a raw HTTP response for `post /v1/sessions/{id}/agentExecute`, but is otherwise + * the same as [SessionService.executeStreaming]. + */ + @MustBeClosed + fun executeStreaming( + id: String, + params: SessionExecuteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> = + executeStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see executeStreaming */ + @MustBeClosed + fun executeStreaming( + params: SessionExecuteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> + /** * Returns a raw HTTP response for `post /v1/sessions/{id}/extract`, but is otherwise the * same as [SessionService.extract]. @@ -238,6 +356,33 @@ interface SessionService { ): HttpResponseFor = 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, + params: SessionExtractParams = SessionExtractParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> = + extractStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + params: SessionExtractParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> + + /** @see extractStreaming */ + @MustBeClosed + fun extractStreaming( + id: String, + requestOptions: RequestOptions, + ): HttpResponseFor> = + extractStreaming(id, SessionExtractParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /v1/sessions/{id}/navigate`, but is otherwise the * same as [SessionService.navigate]. @@ -284,6 +429,33 @@ interface SessionService { ): 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, + params: SessionObserveParams = SessionObserveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> = + observeStreaming(params.toBuilder().id(id).build(), requestOptions) + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming( + params: SessionObserveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> + + /** @see observeStreaming */ + @MustBeClosed + fun observeStreaming( + id: String, + requestOptions: RequestOptions, + ): HttpResponseFor> = + observeStreaming(id, SessionObserveParams.none(), requestOptions) + /** * Returns a raw HTTP response for `post /v1/sessions/start`, but is otherwise the same as * [SessionService.start]. diff --git a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionServiceImpl.kt b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionServiceImpl.kt index c46950e..e25a884 100644 --- a/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionServiceImpl.kt +++ b/stagehand-kotlin-core/src/main/kotlin/com/browserbase/api/services/blocking/SessionServiceImpl.kt @@ -3,17 +3,22 @@ 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.SessionActParams @@ -30,6 +35,7 @@ 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 class SessionServiceImpl internal constructor(private val clientOptions: ClientOptions) : SessionService { @@ -47,6 +53,13 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO // 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 /v1/sessions/{id}/end withRawResponse().end(params, requestOptions).parse() @@ -58,6 +71,13 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO // post /v1/sessions/{id}/agentExecute withRawResponse().execute(params, requestOptions).parse() + override fun executeStreaming( + params: SessionExecuteParams, + requestOptions: RequestOptions, + ): StreamResponse = + // post /v1/sessions/{id}/agentExecute + withRawResponse().executeStreaming(params, requestOptions).parse() + override fun extract( params: SessionExtractParams, requestOptions: RequestOptions, @@ -65,6 +85,13 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO // 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, @@ -79,6 +106,13 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO // 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, @@ -130,6 +164,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()) + 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) @@ -192,6 +268,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()) + 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) @@ -223,6 +341,48 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO } } + 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()) + 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) @@ -285,6 +445,48 @@ class SessionServiceImpl internal constructor(private val clientOptions: ClientO } } + 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()) + 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 + } + } + } + } + private val startHandler: Handler = jsonHandler(clientOptions.jsonMapper) diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/core/handlers/SseHandlerTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/core/handlers/SseHandlerTest.kt new file mode 100644 index 0000000..fb0b17b --- /dev/null +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/core/handlers/SseHandlerTest.kt @@ -0,0 +1,132 @@ +// 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 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.asSequence().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 cannotReuseSequence() { + val response = httpResponse("body") + val streamResponse = sseHandler(jsonMapper()).handle(response) + + val throwable = + streamResponse.use { + it.asSequence().toList() + catchThrowable { it.asSequence().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-kotlin-core/src/test/kotlin/com/browserbase/api/core/handlers/StreamHandlerTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/core/handlers/StreamHandlerTest.kt new file mode 100644 index 0000000..49956a2 --- /dev/null +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/core/handlers/StreamHandlerTest.kt @@ -0,0 +1,92 @@ +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.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.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 + .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.asSequence().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.asSequence().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-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/ActionTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/ActionTest.kt index 9d9a24e..ec6120f 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/ActionTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/ActionTest.kt @@ -16,12 +16,14 @@ internal class ActionTest { .description("Click the submit button") .selector("[data-testid='submit-button']") .addArgument("Hello World") + .backendNodeId(0.0) .method("click") .build() assertThat(action.description()).isEqualTo("Click the submit button") assertThat(action.selector()).isEqualTo("[data-testid='submit-button']") assertThat(action.arguments()).containsExactly("Hello World") + assertThat(action.backendNodeId()).isEqualTo(0.0) assertThat(action.method()).isEqualTo("click") } @@ -33,6 +35,7 @@ internal class ActionTest { .description("Click the submit button") .selector("[data-testid='submit-button']") .addArgument("Hello World") + .backendNodeId(0.0) .method("click") .build() diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/ModelConfigTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/ModelConfigTest.kt index 5f85af8..aa310de 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/ModelConfigTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/ModelConfigTest.kt @@ -13,19 +13,19 @@ import org.junit.jupiter.api.assertThrows internal class ModelConfigTest { @Test - fun ofString() { - val string = "string" + fun ofName() { + val name = "openai/gpt-5-nano" - val modelConfig = ModelConfig.ofString(string) + val modelConfig = ModelConfig.ofName(name) - assertThat(modelConfig.string()).isEqualTo(string) - assertThat(modelConfig.unionMember1()).isNull() + assertThat(modelConfig.name()).isEqualTo(name) + assertThat(modelConfig.modelConfigObject()).isNull() } @Test - fun ofStringRoundtrip() { + fun ofNameRoundtrip() { val jsonMapper = jsonMapper() - val modelConfig = ModelConfig.ofString("string") + val modelConfig = ModelConfig.ofName("openai/gpt-5-nano") val roundtrippedModelConfig = jsonMapper.readValue( @@ -37,29 +37,31 @@ internal class ModelConfigTest { } @Test - fun ofUnionMember1() { - val unionMember1 = - ModelConfig.UnionMember1.builder() - .modelName("modelName") - .apiKey("apiKey") - .baseUrl("https://example.com") + 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() - val modelConfig = ModelConfig.ofUnionMember1(unionMember1) + val modelConfig = ModelConfig.ofModelConfigObject(modelConfigObject) - assertThat(modelConfig.string()).isNull() - assertThat(modelConfig.unionMember1()).isEqualTo(unionMember1) + assertThat(modelConfig.name()).isNull() + assertThat(modelConfig.modelConfigObject()).isEqualTo(modelConfigObject) } @Test - fun ofUnionMember1Roundtrip() { + fun ofModelConfigObjectRoundtrip() { val jsonMapper = jsonMapper() val modelConfig = - ModelConfig.ofUnionMember1( - ModelConfig.UnionMember1.builder() - .modelName("modelName") - .apiKey("apiKey") - .baseUrl("https://example.com") + 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() ) diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActParamsTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActParamsTest.kt index fa5756a..9c95185 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActParamsTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActParamsTest.kt @@ -16,13 +16,13 @@ internal class SessionActParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionActParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionActParams.XStreamResponse.TRUE) .input("Click the login button") .frameId("frameId") .options( SessionActParams.Options.builder() - .model("string") + .model("openai/gpt-5-nano") .timeout(30000.0) .variables( SessionActParams.Options.Variables.builder() @@ -54,13 +54,13 @@ internal class SessionActParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionActParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionActParams.XStreamResponse.TRUE) .input("Click the login button") .frameId("frameId") .options( SessionActParams.Options.builder() - .model("string") + .model("openai/gpt-5-nano") .timeout(30000.0) .variables( SessionActParams.Options.Variables.builder() @@ -78,7 +78,7 @@ internal class SessionActParamsTest { Headers.builder() .put("x-language", "typescript") .put("x-sdk-version", "3.0.6") - .put("x-sent-at", "2025-01-15T10:30:00.000Z") + .put("x-sent-at", "2025-01-15T10:30:00Z") .put("x-stream-response", "true") .build() ) @@ -104,13 +104,13 @@ internal class SessionActParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionActParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionActParams.XStreamResponse.TRUE) .input("Click the login button") .frameId("frameId") .options( SessionActParams.Options.builder() - .model("string") + .model("openai/gpt-5-nano") .timeout(30000.0) .variables( SessionActParams.Options.Variables.builder() @@ -129,7 +129,7 @@ internal class SessionActParamsTest { assertThat(body.options()) .isEqualTo( SessionActParams.Options.builder() - .model("string") + .model("openai/gpt-5-nano") .timeout(30000.0) .variables( SessionActParams.Options.Variables.builder() diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActResponseTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActResponseTest.kt index 000d7fd..52cc63c 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActResponseTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionActResponseTest.kt @@ -19,10 +19,11 @@ internal class SessionActResponseTest { SessionActResponse.Data.Result.builder() .actionDescription("Clicked button with text 'Login'") .addAction( - Action.builder() + SessionActResponse.Data.Result.Action.builder() .description("Click the submit button") .selector("[data-testid='submit-button']") .addArgument("Hello World") + .backendNodeId(0.0) .method("click") .build() ) @@ -33,7 +34,7 @@ internal class SessionActResponseTest { .actionId("actionId") .build() ) - .success(SessionActResponse.Success.TRUE) + .success(true) .build() assertThat(sessionActResponse.data()) @@ -43,10 +44,11 @@ internal class SessionActResponseTest { SessionActResponse.Data.Result.builder() .actionDescription("Clicked button with text 'Login'") .addAction( - Action.builder() + SessionActResponse.Data.Result.Action.builder() .description("Click the submit button") .selector("[data-testid='submit-button']") .addArgument("Hello World") + .backendNodeId(0.0) .method("click") .build() ) @@ -57,7 +59,7 @@ internal class SessionActResponseTest { .actionId("actionId") .build() ) - assertThat(sessionActResponse.success()).isEqualTo(SessionActResponse.Success.TRUE) + assertThat(sessionActResponse.success()).isEqualTo(true) } @Test @@ -71,10 +73,11 @@ internal class SessionActResponseTest { SessionActResponse.Data.Result.builder() .actionDescription("Clicked button with text 'Login'") .addAction( - Action.builder() + SessionActResponse.Data.Result.Action.builder() .description("Click the submit button") .selector("[data-testid='submit-button']") .addArgument("Hello World") + .backendNodeId(0.0) .method("click") .build() ) @@ -85,7 +88,7 @@ internal class SessionActResponseTest { .actionId("actionId") .build() ) - .success(SessionActResponse.Success.TRUE) + .success(true) .build() val roundtrippedSessionActResponse = diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndParamsTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndParamsTest.kt index 14faac3..bc50272 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndParamsTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndParamsTest.kt @@ -15,7 +15,7 @@ internal class SessionEndParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionEndParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionEndParams.XStreamResponse.TRUE) .build() } @@ -36,7 +36,7 @@ internal class SessionEndParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionEndParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionEndParams.XStreamResponse.TRUE) .build() @@ -47,7 +47,7 @@ internal class SessionEndParamsTest { Headers.builder() .put("x-language", "typescript") .put("x-sdk-version", "3.0.6") - .put("x-sent-at", "2025-01-15T10:30:00.000Z") + .put("x-sent-at", "2025-01-15T10:30:00Z") .put("x-stream-response", "true") .build() ) diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndResponseTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndResponseTest.kt index 27a2a36..041295f 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndResponseTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionEndResponseTest.kt @@ -11,17 +11,15 @@ internal class SessionEndResponseTest { @Test fun create() { - val sessionEndResponse = - SessionEndResponse.builder().success(SessionEndResponse.Success.TRUE).build() + val sessionEndResponse = SessionEndResponse.builder().success(true).build() - assertThat(sessionEndResponse.success()).isEqualTo(SessionEndResponse.Success.TRUE) + assertThat(sessionEndResponse.success()).isEqualTo(true) } @Test fun roundtrip() { val jsonMapper = jsonMapper() - val sessionEndResponse = - SessionEndResponse.builder().success(SessionEndResponse.Success.TRUE).build() + val sessionEndResponse = SessionEndResponse.builder().success(true).build() val roundtrippedSessionEndResponse = jsonMapper.readValue( diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteParamsTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteParamsTest.kt index 986250a..cb76d26 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteParamsTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteParamsTest.kt @@ -15,12 +15,13 @@ internal class SessionExecuteParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionExecuteParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionExecuteParams.XStreamResponse.TRUE) .agentConfig( SessionExecuteParams.AgentConfig.builder() .cua(true) - .model("string") + .model("openai/gpt-5-nano") + .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) .systemPrompt("systemPrompt") .build() ) @@ -64,12 +65,13 @@ internal class SessionExecuteParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionExecuteParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionExecuteParams.XStreamResponse.TRUE) .agentConfig( SessionExecuteParams.AgentConfig.builder() .cua(true) - .model("string") + .model("openai/gpt-5-nano") + .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) .systemPrompt("systemPrompt") .build() ) @@ -92,7 +94,7 @@ internal class SessionExecuteParamsTest { Headers.builder() .put("x-language", "typescript") .put("x-sdk-version", "3.0.6") - .put("x-sent-at", "2025-01-15T10:30:00.000Z") + .put("x-sent-at", "2025-01-15T10:30:00Z") .put("x-stream-response", "true") .build() ) @@ -125,12 +127,13 @@ internal class SessionExecuteParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionExecuteParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionExecuteParams.XStreamResponse.TRUE) .agentConfig( SessionExecuteParams.AgentConfig.builder() .cua(true) - .model("string") + .model("openai/gpt-5-nano") + .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) .systemPrompt("systemPrompt") .build() ) @@ -152,7 +155,8 @@ internal class SessionExecuteParamsTest { .isEqualTo( SessionExecuteParams.AgentConfig.builder() .cua(true) - .model("string") + .model("openai/gpt-5-nano") + .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) .systemPrompt("systemPrompt") .build() ) diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteResponseTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteResponseTest.kt index d2fb4d9..85a2099 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteResponseTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExecuteResponseTest.kt @@ -51,7 +51,7 @@ internal class SessionExecuteResponseTest { ) .build() ) - .success(SessionExecuteResponse.Success.TRUE) + .success(true) .build() assertThat(sessionExecuteResponse.data()) @@ -92,7 +92,7 @@ internal class SessionExecuteResponseTest { ) .build() ) - assertThat(sessionExecuteResponse.success()).isEqualTo(SessionExecuteResponse.Success.TRUE) + assertThat(sessionExecuteResponse.success()).isEqualTo(true) } @Test @@ -137,7 +137,7 @@ internal class SessionExecuteResponseTest { ) .build() ) - .success(SessionExecuteResponse.Success.TRUE) + .success(true) .build() val roundtrippedSessionExecuteResponse = diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractParamsTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractParamsTest.kt index d05ff06..9699c80 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractParamsTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractParamsTest.kt @@ -16,13 +16,13 @@ internal class SessionExtractParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionExtractParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .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("string") + .model("openai/gpt-5-nano") .selector("#main-content") .timeout(30000.0) .build() @@ -52,13 +52,13 @@ internal class SessionExtractParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionExtractParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .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("string") + .model("openai/gpt-5-nano") .selector("#main-content") .timeout(30000.0) .build() @@ -77,7 +77,7 @@ internal class SessionExtractParamsTest { Headers.builder() .put("x-language", "typescript") .put("x-sdk-version", "3.0.6") - .put("x-sent-at", "2025-01-15T10:30:00.000Z") + .put("x-sent-at", "2025-01-15T10:30:00Z") .put("x-stream-response", "true") .build() ) @@ -100,13 +100,13 @@ internal class SessionExtractParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionExtractParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .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("string") + .model("openai/gpt-5-nano") .selector("#main-content") .timeout(30000.0) .build() @@ -126,7 +126,7 @@ internal class SessionExtractParamsTest { assertThat(body.options()) .isEqualTo( SessionExtractParams.Options.builder() - .model("string") + .model("openai/gpt-5-nano") .selector("#main-content") .timeout(30000.0) .build() diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractResponseTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractResponseTest.kt index 6cf7056..3f055cd 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractResponseTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionExtractResponseTest.kt @@ -20,7 +20,7 @@ internal class SessionExtractResponseTest { .actionId("actionId") .build() ) - .success(SessionExtractResponse.Success.TRUE) + .success(true) .build() assertThat(sessionExtractResponse.data()) @@ -30,7 +30,7 @@ internal class SessionExtractResponseTest { .actionId("actionId") .build() ) - assertThat(sessionExtractResponse.success()).isEqualTo(SessionExtractResponse.Success.TRUE) + assertThat(sessionExtractResponse.success()).isEqualTo(true) } @Test @@ -44,7 +44,7 @@ internal class SessionExtractResponseTest { .actionId("actionId") .build() ) - .success(SessionExtractResponse.Success.TRUE) + .success(true) .build() val roundtrippedSessionExtractResponse = diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateParamsTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateParamsTest.kt index 6b8229a..cc794c9 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateParamsTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateParamsTest.kt @@ -15,7 +15,7 @@ internal class SessionNavigateParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionNavigateParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionNavigateParams.XStreamResponse.TRUE) .url("https://example.com") .frameId("frameId") @@ -26,6 +26,7 @@ internal class SessionNavigateParamsTest { .waitUntil(SessionNavigateParams.Options.WaitUntil.NETWORKIDLE) .build() ) + .streamResponse(true) .build() } @@ -49,7 +50,7 @@ internal class SessionNavigateParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionNavigateParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionNavigateParams.XStreamResponse.TRUE) .url("https://example.com") .frameId("frameId") @@ -60,6 +61,7 @@ internal class SessionNavigateParamsTest { .waitUntil(SessionNavigateParams.Options.WaitUntil.NETWORKIDLE) .build() ) + .streamResponse(true) .build() val headers = params._headers() @@ -69,7 +71,7 @@ internal class SessionNavigateParamsTest { Headers.builder() .put("x-language", "typescript") .put("x-sdk-version", "3.0.6") - .put("x-sent-at", "2025-01-15T10:30:00.000Z") + .put("x-sent-at", "2025-01-15T10:30:00Z") .put("x-stream-response", "true") .build() ) @@ -95,7 +97,7 @@ internal class SessionNavigateParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionNavigateParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionNavigateParams.XStreamResponse.TRUE) .url("https://example.com") .frameId("frameId") @@ -106,6 +108,7 @@ internal class SessionNavigateParamsTest { .waitUntil(SessionNavigateParams.Options.WaitUntil.NETWORKIDLE) .build() ) + .streamResponse(true) .build() val body = params._body() @@ -120,6 +123,7 @@ internal class SessionNavigateParamsTest { .waitUntil(SessionNavigateParams.Options.WaitUntil.NETWORKIDLE) .build() ) + assertThat(body.streamResponse()).isEqualTo(true) } @Test diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponseTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponseTest.kt index e02a0e3..bca29d9 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponseTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionNavigateResponseTest.kt @@ -20,7 +20,7 @@ internal class SessionNavigateResponseTest { .actionId("actionId") .build() ) - .success(SessionNavigateResponse.Success.TRUE) + .success(true) .build() assertThat(sessionNavigateResponse.data()) @@ -30,8 +30,7 @@ internal class SessionNavigateResponseTest { .actionId("actionId") .build() ) - assertThat(sessionNavigateResponse.success()) - .isEqualTo(SessionNavigateResponse.Success.TRUE) + assertThat(sessionNavigateResponse.success()).isEqualTo(true) } @Test @@ -45,7 +44,7 @@ internal class SessionNavigateResponseTest { .actionId("actionId") .build() ) - .success(SessionNavigateResponse.Success.TRUE) + .success(true) .build() val roundtrippedSessionNavigateResponse = diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveParamsTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveParamsTest.kt index df1e03e..16cd795 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveParamsTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveParamsTest.kt @@ -15,13 +15,13 @@ internal class SessionObserveParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionObserveParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .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("string") + .model("openai/gpt-5-nano") .selector("nav") .timeout(30000.0) .build() @@ -46,13 +46,13 @@ internal class SessionObserveParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionObserveParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .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("string") + .model("openai/gpt-5-nano") .selector("nav") .timeout(30000.0) .build() @@ -66,7 +66,7 @@ internal class SessionObserveParamsTest { Headers.builder() .put("x-language", "typescript") .put("x-sdk-version", "3.0.6") - .put("x-sent-at", "2025-01-15T10:30:00.000Z") + .put("x-sent-at", "2025-01-15T10:30:00Z") .put("x-stream-response", "true") .build() ) @@ -89,13 +89,13 @@ internal class SessionObserveParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionObserveParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .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("string") + .model("openai/gpt-5-nano") .selector("nav") .timeout(30000.0) .build() @@ -109,7 +109,7 @@ internal class SessionObserveParamsTest { assertThat(body.options()) .isEqualTo( SessionObserveParams.Options.builder() - .model("string") + .model("openai/gpt-5-nano") .selector("nav") .timeout(30000.0) .build() diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveResponseTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveResponseTest.kt index e3d72c3..938fff3 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveResponseTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionObserveResponseTest.kt @@ -16,34 +16,36 @@ internal class SessionObserveResponseTest { .data( SessionObserveResponse.Data.builder() .addResult( - Action.builder() + 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(SessionObserveResponse.Success.TRUE) + .success(true) .build() assertThat(sessionObserveResponse.data()) .isEqualTo( SessionObserveResponse.Data.builder() .addResult( - Action.builder() + 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(SessionObserveResponse.Success.TRUE) + assertThat(sessionObserveResponse.success()).isEqualTo(true) } @Test @@ -54,17 +56,18 @@ internal class SessionObserveResponseTest { .data( SessionObserveResponse.Data.builder() .addResult( - Action.builder() + 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(SessionObserveResponse.Success.TRUE) + .success(true) .build() val roundtrippedSessionObserveResponse = diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartParamsTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartParamsTest.kt index 92c3d88..42f1ff0 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartParamsTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartParamsTest.kt @@ -15,10 +15,10 @@ internal class SessionStartParamsTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -148,7 +148,6 @@ internal class SessionStartParamsTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -164,10 +163,10 @@ internal class SessionStartParamsTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -301,7 +300,6 @@ internal class SessionStartParamsTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -317,7 +315,7 @@ internal class SessionStartParamsTest { Headers.builder() .put("x-language", "typescript") .put("x-sdk-version", "3.0.6") - .put("x-sent-at", "2025-01-15T10:30:00.000Z") + .put("x-sent-at", "2025-01-15T10:30:00Z") .put("x-stream-response", "true") .build() ) @@ -338,10 +336,10 @@ internal class SessionStartParamsTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -475,7 +473,6 @@ internal class SessionStartParamsTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -487,7 +484,7 @@ internal class SessionStartParamsTest { val body = params._body() assertThat(body.modelName()).isEqualTo("gpt-4o") - assertThat(body.actTimeoutMs()).isEqualTo(30000.0) + assertThat(body.actTimeoutMs()).isEqualTo(0.0) assertThat(body.browser()) .isEqualTo( SessionStartParams.Browser.builder() @@ -619,7 +616,6 @@ internal class SessionStartParamsTest { .build() ) assertThat(body.browserbaseSessionId()).isEqualTo("browserbaseSessionID") - assertThat(body.debugDom()).isEqualTo(true) assertThat(body.domSettleTimeoutMs()).isEqualTo(5000.0) assertThat(body.experimental()).isEqualTo(true) assertThat(body.selfHeal()).isEqualTo(true) diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartResponseTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartResponseTest.kt index 717d6d6..0ae6883 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartResponseTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/SessionStartResponseTest.kt @@ -17,9 +17,10 @@ internal class SessionStartResponseTest { SessionStartResponse.Data.builder() .available(true) .sessionId("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .cdpUrl("wss://connect.browserbase.com/?signingKey=abc123") .build() ) - .success(SessionStartResponse.Success.TRUE) + .success(true) .build() assertThat(sessionStartResponse.data()) @@ -27,9 +28,10 @@ internal class SessionStartResponseTest { SessionStartResponse.Data.builder() .available(true) .sessionId("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .cdpUrl("wss://connect.browserbase.com/?signingKey=abc123") .build() ) - assertThat(sessionStartResponse.success()).isEqualTo(SessionStartResponse.Success.TRUE) + assertThat(sessionStartResponse.success()).isEqualTo(true) } @Test @@ -41,9 +43,10 @@ internal class SessionStartResponseTest { SessionStartResponse.Data.builder() .available(true) .sessionId("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") + .cdpUrl("wss://connect.browserbase.com/?signingKey=abc123") .build() ) - .success(SessionStartResponse.Success.TRUE) + .success(true) .build() val roundtrippedSessionStartResponse = diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/StreamEventTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/models/sessions/StreamEventTest.kt new file mode 100644 index 0000000..9d58bf8 --- /dev/null +++ b/stagehand-kotlin-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-kotlin-core/src/test/kotlin/com/browserbase/api/services/ErrorHandlingTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/services/ErrorHandlingTest.kt index 87af3f1..f704c1a 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/services/ErrorHandlingTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/services/ErrorHandlingTest.kt @@ -77,10 +77,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -225,7 +225,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -257,10 +256,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -405,7 +404,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -437,10 +435,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -585,7 +583,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -617,10 +614,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -765,7 +762,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -797,10 +793,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -945,7 +941,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -977,10 +972,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -1125,7 +1120,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -1157,10 +1151,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -1305,7 +1299,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -1337,10 +1330,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -1485,7 +1478,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -1517,10 +1509,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -1665,7 +1657,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -1697,10 +1688,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -1845,7 +1836,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -1877,10 +1867,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -2025,7 +2015,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -2057,10 +2046,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -2205,7 +2194,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -2237,10 +2225,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -2385,7 +2373,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -2417,10 +2404,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -2565,7 +2552,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -2597,10 +2583,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -2745,7 +2731,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -2777,10 +2762,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -2925,7 +2910,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -2955,10 +2939,10 @@ internal class ErrorHandlingTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -3103,7 +3087,6 @@ internal class ErrorHandlingTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/services/ServiceParamsTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/services/ServiceParamsTest.kt index ecf30b0..2243046 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/services/ServiceParamsTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/services/ServiceParamsTest.kt @@ -50,10 +50,10 @@ internal class ServiceParamsTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -187,7 +187,6 @@ internal class ServiceParamsTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) @@ -219,13 +218,13 @@ internal class ServiceParamsTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionActParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionActParams.XStreamResponse.TRUE) .input("Click the login button") .frameId("frameId") .options( SessionActParams.Options.builder() - .model("string") + .model("openai/gpt-5-nano") .timeout(30000.0) .variables( SessionActParams.Options.Variables.builder() diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/services/async/SessionServiceAsyncTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/services/async/SessionServiceAsyncTest.kt index 95ec115..86f4c7e 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/services/async/SessionServiceAsyncTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/services/async/SessionServiceAsyncTest.kt @@ -38,13 +38,13 @@ internal class SessionServiceAsyncTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionActParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionActParams.XStreamResponse.TRUE) .input("Click the login button") .frameId("frameId") .options( SessionActParams.Options.builder() - .model("string") + .model("openai/gpt-5-nano") .timeout(30000.0) .variables( SessionActParams.Options.Variables.builder() @@ -77,7 +77,7 @@ internal class SessionServiceAsyncTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionEndParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionEndParams.XStreamResponse.TRUE) .build() ) @@ -103,12 +103,13 @@ internal class SessionServiceAsyncTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionExecuteParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionExecuteParams.XStreamResponse.TRUE) .agentConfig( SessionExecuteParams.AgentConfig.builder() .cua(true) - .model("string") + .model("openai/gpt-5-nano") + .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) .systemPrompt("systemPrompt") .build() ) @@ -146,13 +147,13 @@ internal class SessionServiceAsyncTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionExtractParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .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("string") + .model("openai/gpt-5-nano") .selector("#main-content") .timeout(30000.0) .build() @@ -186,7 +187,7 @@ internal class SessionServiceAsyncTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionNavigateParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionNavigateParams.XStreamResponse.TRUE) .url("https://example.com") .frameId("frameId") @@ -197,6 +198,7 @@ internal class SessionServiceAsyncTest { .waitUntil(SessionNavigateParams.Options.WaitUntil.NETWORKIDLE) .build() ) + .streamResponse(true) .build() ) @@ -221,13 +223,13 @@ internal class SessionServiceAsyncTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionObserveParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .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("string") + .model("openai/gpt-5-nano") .selector("nav") .timeout(30000.0) .build() @@ -255,10 +257,10 @@ internal class SessionServiceAsyncTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -395,7 +397,6 @@ internal class SessionServiceAsyncTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) diff --git a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/services/blocking/SessionServiceTest.kt b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/services/blocking/SessionServiceTest.kt index 3f62ca6..dbb1dc5 100644 --- a/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/services/blocking/SessionServiceTest.kt +++ b/stagehand-kotlin-core/src/test/kotlin/com/browserbase/api/services/blocking/SessionServiceTest.kt @@ -38,13 +38,13 @@ internal class SessionServiceTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionActParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionActParams.XStreamResponse.TRUE) .input("Click the login button") .frameId("frameId") .options( SessionActParams.Options.builder() - .model("string") + .model("openai/gpt-5-nano") .timeout(30000.0) .variables( SessionActParams.Options.Variables.builder() @@ -59,6 +59,47 @@ internal class SessionServiceTest { 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("username", JsonValue.from("john_doe")) + .build() + ) + .build() + ) + .build() + ) + + responseStreamResponse.use { + responseStreamResponse.asSequence().forEach { response -> response.validate() } + } + } + @Disabled("Prism tests are disabled") @Test fun end() { @@ -77,7 +118,7 @@ internal class SessionServiceTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionEndParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionEndParams.XStreamResponse.TRUE) .build() ) @@ -103,12 +144,13 @@ internal class SessionServiceTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionExecuteParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionExecuteParams.XStreamResponse.TRUE) .agentConfig( SessionExecuteParams.AgentConfig.builder() .cua(true) - .model("string") + .model("openai/gpt-5-nano") + .provider(SessionExecuteParams.AgentConfig.Provider.OPENAI) .systemPrompt("systemPrompt") .build() ) @@ -128,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.asSequence().forEach { response -> response.validate() } + } + } + @Disabled("Prism tests are disabled") @Test fun extract() { @@ -146,13 +234,13 @@ internal class SessionServiceTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionExtractParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .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("string") + .model("openai/gpt-5-nano") .selector("#main-content") .timeout(30000.0) .build() @@ -168,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.asSequence().forEach { response -> response.validate() } + } + } + @Disabled("Prism tests are disabled") @Test fun navigate() { @@ -186,7 +316,7 @@ internal class SessionServiceTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionNavigateParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionNavigateParams.XStreamResponse.TRUE) .url("https://example.com") .frameId("frameId") @@ -197,6 +327,7 @@ internal class SessionServiceTest { .waitUntil(SessionNavigateParams.Options.WaitUntil.NETWORKIDLE) .build() ) + .streamResponse(true) .build() ) @@ -221,13 +352,13 @@ internal class SessionServiceTest { .id("c4dbf3a9-9a58-4b22-8a1c-9f20f9f9e123") .xLanguage(SessionObserveParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .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("string") + .model("openai/gpt-5-nano") .selector("nav") .timeout(30000.0) .build() @@ -238,6 +369,43 @@ internal class SessionServiceTest { 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.asSequence().forEach { response -> response.validate() } + } + } + @Disabled("Prism tests are disabled") @Test fun start() { @@ -255,10 +423,10 @@ internal class SessionServiceTest { SessionStartParams.builder() .xLanguage(SessionStartParams.XLanguage.TYPESCRIPT) .xSdkVersion("3.0.6") - .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00.000Z")) + .xSentAt(OffsetDateTime.parse("2025-01-15T10:30:00Z")) .xStreamResponse(SessionStartParams.XStreamResponse.TRUE) .modelName("gpt-4o") - .actTimeoutMs(30000.0) + .actTimeoutMs(0.0) .browser( SessionStartParams.Browser.builder() .cdpUrl("ws://localhost:9222") @@ -395,7 +563,6 @@ internal class SessionServiceTest { .build() ) .browserbaseSessionId("browserbaseSessionID") - .debugDom(true) .domSettleTimeoutMs(5000.0) .experimental(true) .selfHeal(true) diff --git a/stagehand-kotlin-proguard-test/src/test/kotlin/com/browserbase/api/proguard/ProGuardCompatibilityTest.kt b/stagehand-kotlin-proguard-test/src/test/kotlin/com/browserbase/api/proguard/ProGuardCompatibilityTest.kt index 316c028..8101c00 100644 --- a/stagehand-kotlin-proguard-test/src/test/kotlin/com/browserbase/api/proguard/ProGuardCompatibilityTest.kt +++ b/stagehand-kotlin-proguard-test/src/test/kotlin/com/browserbase/api/proguard/ProGuardCompatibilityTest.kt @@ -64,6 +64,7 @@ internal class ProGuardCompatibilityTest { .description("Click the submit button") .selector("[data-testid='submit-button']") .addArgument("Hello World") + .backendNodeId(0.0) .method("click") .build() @@ -76,7 +77,7 @@ internal class ProGuardCompatibilityTest { @Test fun modelConfigRoundtrip() { val jsonMapper = jsonMapper() - val modelConfig = ModelConfig.ofString("string") + val modelConfig = ModelConfig.ofName("openai/gpt-5-nano") val roundtrippedModelConfig = jsonMapper.readValue(