From 57da3096437fe210865bab3a7560eff50d20feef Mon Sep 17 00:00:00 2001 From: stlc-bot Date: Mon, 20 Jul 2026 10:12:06 +0000 Subject: [PATCH] feat(runs): add GET /v2/runs/{run_id}/url endpoint and SDK runs.get_url method Stainless-Generated-From: 12ef224c44ef834db59a6216eb1010f87947b165 --- .stats.yml | 2 +- .../smith/models/runs/RunGetUrlParams.kt | 256 ++++++++++++++++++ .../smith/models/runs/RunGetUrlResponse.kt | 160 +++++++++++ .../smith/services/async/RunServiceAsync.kt | 55 ++++ .../services/async/RunServiceAsyncImpl.kt | 42 +++ .../smith/services/blocking/RunService.kt | 54 ++++ .../smith/services/blocking/RunServiceImpl.kt | 39 +++ .../smith/models/runs/RunGetUrlParamsTest.kt | 76 ++++++ .../models/runs/RunGetUrlResponseTest.kt | 32 +++ .../services/async/RunServiceAsyncTest.kt | 25 ++ .../smith/services/blocking/RunServiceTest.kt | 21 ++ 11 files changed, 761 insertions(+), 1 deletion(-) create mode 100644 langsmith-java-core/src/main/kotlin/com/langchain/smith/models/runs/RunGetUrlParams.kt create mode 100644 langsmith-java-core/src/main/kotlin/com/langchain/smith/models/runs/RunGetUrlResponse.kt create mode 100644 langsmith-java-core/src/test/kotlin/com/langchain/smith/models/runs/RunGetUrlParamsTest.kt create mode 100644 langsmith-java-core/src/test/kotlin/com/langchain/smith/models/runs/RunGetUrlResponseTest.kt diff --git a/.stats.yml b/.stats.yml index c7af722a..9f97df3f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 148 +configured_endpoints: 149 diff --git a/langsmith-java-core/src/main/kotlin/com/langchain/smith/models/runs/RunGetUrlParams.kt b/langsmith-java-core/src/main/kotlin/com/langchain/smith/models/runs/RunGetUrlParams.kt new file mode 100644 index 00000000..7e2b45da --- /dev/null +++ b/langsmith-java-core/src/main/kotlin/com/langchain/smith/models/runs/RunGetUrlParams.kt @@ -0,0 +1,256 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.langchain.smith.models.runs + +import com.langchain.smith.core.Params +import com.langchain.smith.core.checkRequired +import com.langchain.smith.core.http.Headers +import com.langchain.smith.core.http.QueryParams +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * Returns the URL to view a specific run in the LangSmith UI. The caller must supply the run's + * project_id and trace_id as query parameters; start_time is optional. + */ +class RunGetUrlParams +private constructor( + private val runId: String?, + private val projectId: String, + private val traceId: String, + private val startTime: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun runId(): Optional = Optional.ofNullable(runId) + + /** Project (session) UUID */ + fun projectId(): String = projectId + + /** Trace UUID */ + fun traceId(): String = traceId + + /** Run start time in RFC3339 format; omit if unknown */ + fun startTime(): Optional = Optional.ofNullable(startTime) + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [RunGetUrlParams]. + * + * The following fields are required: + * ```java + * .projectId() + * .traceId() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [RunGetUrlParams]. */ + class Builder internal constructor() { + + private var runId: String? = null + private var projectId: String? = null + private var traceId: String? = null + private var startTime: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(runGetUrlParams: RunGetUrlParams) = apply { + runId = runGetUrlParams.runId + projectId = runGetUrlParams.projectId + traceId = runGetUrlParams.traceId + startTime = runGetUrlParams.startTime + additionalHeaders = runGetUrlParams.additionalHeaders.toBuilder() + additionalQueryParams = runGetUrlParams.additionalQueryParams.toBuilder() + } + + fun runId(runId: String?) = apply { this.runId = runId } + + /** Alias for calling [Builder.runId] with `runId.orElse(null)`. */ + fun runId(runId: Optional) = runId(runId.getOrNull()) + + /** Project (session) UUID */ + fun projectId(projectId: String) = apply { this.projectId = projectId } + + /** Trace UUID */ + fun traceId(traceId: String) = apply { this.traceId = traceId } + + /** Run start time in RFC3339 format; omit if unknown */ + fun startTime(startTime: String?) = apply { this.startTime = startTime } + + /** Alias for calling [Builder.startTime] with `startTime.orElse(null)`. */ + fun startTime(startTime: Optional) = startTime(startTime.getOrNull()) + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [RunGetUrlParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .projectId() + * .traceId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): RunGetUrlParams = + RunGetUrlParams( + runId, + checkRequired("projectId", projectId), + checkRequired("traceId", traceId), + startTime, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> runId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + put("project_id", projectId) + put("trace_id", traceId) + startTime?.let { put("start_time", it) } + putAll(additionalQueryParams) + } + .build() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is RunGetUrlParams && + runId == other.runId && + projectId == other.projectId && + traceId == other.traceId && + startTime == other.startTime && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(runId, projectId, traceId, startTime, additionalHeaders, additionalQueryParams) + + override fun toString() = + "RunGetUrlParams{runId=$runId, projectId=$projectId, traceId=$traceId, startTime=$startTime, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/langsmith-java-core/src/main/kotlin/com/langchain/smith/models/runs/RunGetUrlResponse.kt b/langsmith-java-core/src/main/kotlin/com/langchain/smith/models/runs/RunGetUrlResponse.kt new file mode 100644 index 00000000..977e87f3 --- /dev/null +++ b/langsmith-java-core/src/main/kotlin/com/langchain/smith/models/runs/RunGetUrlResponse.kt @@ -0,0 +1,160 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.langchain.smith.models.runs + +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.langchain.smith.core.ExcludeMissing +import com.langchain.smith.core.JsonField +import com.langchain.smith.core.JsonMissing +import com.langchain.smith.core.JsonValue +import com.langchain.smith.errors.LangChainInvalidDataException +import java.util.Collections +import java.util.Objects +import java.util.Optional + +class RunGetUrlResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val url: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("url") @ExcludeMissing url: JsonField = JsonMissing.of() + ) : this(url, mutableMapOf()) + + /** + * @throws LangChainInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun url(): Optional = url.getOptional("url") + + /** + * Returns the raw JSON value of [url]. + * + * Unlike [url], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("url") @ExcludeMissing fun _url(): JsonField = url + + @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 [RunGetUrlResponse]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [RunGetUrlResponse]. */ + class Builder internal constructor() { + + private var url: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(runGetUrlResponse: RunGetUrlResponse) = apply { + url = runGetUrlResponse.url + additionalProperties = runGetUrlResponse.additionalProperties.toMutableMap() + } + + fun url(url: String) = url(JsonField.of(url)) + + /** + * Sets [Builder.url] to an arbitrary JSON value. + * + * You should usually call [Builder.url] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun url(url: JsonField) = apply { this.url = url } + + fun 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 [RunGetUrlResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): RunGetUrlResponse = RunGetUrlResponse(url, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws LangChainInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): RunGetUrlResponse = apply { + if (validated) { + return@apply + } + + url() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: LangChainInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic internal fun validity(): Int = (if (url.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is RunGetUrlResponse && + url == other.url && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(url, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "RunGetUrlResponse{url=$url, additionalProperties=$additionalProperties}" +} diff --git a/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/async/RunServiceAsync.kt b/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/async/RunServiceAsync.kt index 60924f86..97d5ac6d 100644 --- a/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/async/RunServiceAsync.kt +++ b/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/async/RunServiceAsync.kt @@ -8,6 +8,8 @@ import com.langchain.smith.core.http.HttpResponseFor import com.langchain.smith.models.runs.Run import com.langchain.smith.models.runs.RunCreateParams import com.langchain.smith.models.runs.RunCreateResponse +import com.langchain.smith.models.runs.RunGetUrlParams +import com.langchain.smith.models.runs.RunGetUrlResponse import com.langchain.smith.models.runs.RunIngest import com.langchain.smith.models.runs.RunIngestBatchParams import com.langchain.smith.models.runs.RunIngestBatchResponse @@ -102,6 +104,31 @@ interface RunServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture + /** + * Returns the URL to view a specific run in the LangSmith UI. The caller must supply the run's + * project_id and trace_id as query parameters; start_time is optional. + */ + fun getUrl(runId: String, params: RunGetUrlParams): CompletableFuture = + getUrl(runId, params, RequestOptions.none()) + + /** @see getUrl */ + fun getUrl( + runId: String, + params: RunGetUrlParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + getUrl(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see getUrl */ + fun getUrl(params: RunGetUrlParams): CompletableFuture = + getUrl(params, RequestOptions.none()) + + /** @see getUrl */ + fun getUrl( + params: RunGetUrlParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + /** * Ingests a batch of runs in a single JSON payload. The payload must have `post` and/or `patch` * arrays containing run objects. Prefer this endpoint over single‑run ingestion when submitting @@ -430,6 +457,34 @@ interface RunServiceAsync { requestOptions: RequestOptions = RequestOptions.none(), ): CompletableFuture> + /** + * Returns a raw HTTP response for `get /v2/runs/{run_id}/url`, but is otherwise the same as + * [RunServiceAsync.getUrl]. + */ + fun getUrl( + runId: String, + params: RunGetUrlParams, + ): CompletableFuture> = + getUrl(runId, params, RequestOptions.none()) + + /** @see getUrl */ + fun getUrl( + runId: String, + params: RunGetUrlParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + getUrl(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see getUrl */ + fun getUrl(params: RunGetUrlParams): CompletableFuture> = + getUrl(params, RequestOptions.none()) + + /** @see getUrl */ + fun getUrl( + params: RunGetUrlParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + /** * Returns a raw HTTP response for `post /runs/batch`, but is otherwise the same as * [RunServiceAsync.ingestBatch]. diff --git a/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/async/RunServiceAsyncImpl.kt b/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/async/RunServiceAsyncImpl.kt index 6be8c03f..064001ac 100644 --- a/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/async/RunServiceAsyncImpl.kt +++ b/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/async/RunServiceAsyncImpl.kt @@ -26,6 +26,8 @@ import com.langchain.smith.models.info.InfoListResponse import com.langchain.smith.models.runs.Run import com.langchain.smith.models.runs.RunCreateParams import com.langchain.smith.models.runs.RunCreateResponse +import com.langchain.smith.models.runs.RunGetUrlParams +import com.langchain.smith.models.runs.RunGetUrlResponse import com.langchain.smith.models.runs.RunIngest import com.langchain.smith.models.runs.RunIngestBatchParams import com.langchain.smith.models.runs.RunIngestBatchResponse @@ -210,6 +212,13 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client return CompletableFuture.completedFuture(null) } + override fun getUrl( + params: RunGetUrlParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // get /v2/runs/{run_id}/url + withRawResponse().getUrl(params, requestOptions).thenApply { it.parse() } + override fun ingestBatch( params: RunIngestBatchParams, requestOptions: RequestOptions, @@ -419,6 +428,39 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client } } + private val getUrlHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun getUrl( + params: RunGetUrlParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v2", "runs", params._pathParam(0), "url") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { getUrlHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + private val ingestBatchHandler: Handler = jsonHandler(clientOptions.jsonMapper) diff --git a/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/blocking/RunService.kt b/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/blocking/RunService.kt index 0f3b4d5b..dfe9c093 100644 --- a/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/blocking/RunService.kt +++ b/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/blocking/RunService.kt @@ -9,6 +9,8 @@ import com.langchain.smith.core.http.HttpResponseFor import com.langchain.smith.models.runs.Run import com.langchain.smith.models.runs.RunCreateParams import com.langchain.smith.models.runs.RunCreateResponse +import com.langchain.smith.models.runs.RunGetUrlParams +import com.langchain.smith.models.runs.RunGetUrlResponse import com.langchain.smith.models.runs.RunIngest import com.langchain.smith.models.runs.RunIngestBatchParams import com.langchain.smith.models.runs.RunIngestBatchResponse @@ -96,6 +98,29 @@ interface RunService { /** @see update */ fun update(params: RunUpdateParams, requestOptions: RequestOptions = RequestOptions.none()) + /** + * Returns the URL to view a specific run in the LangSmith UI. The caller must supply the run's + * project_id and trace_id as query parameters; start_time is optional. + */ + fun getUrl(runId: String, params: RunGetUrlParams): RunGetUrlResponse = + getUrl(runId, params, RequestOptions.none()) + + /** @see getUrl */ + fun getUrl( + runId: String, + params: RunGetUrlParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): RunGetUrlResponse = getUrl(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see getUrl */ + fun getUrl(params: RunGetUrlParams): RunGetUrlResponse = getUrl(params, RequestOptions.none()) + + /** @see getUrl */ + fun getUrl( + params: RunGetUrlParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): RunGetUrlResponse + /** * Ingests a batch of runs in a single JSON payload. The payload must have `post` and/or `patch` * arrays containing run objects. Prefer this endpoint over single‑run ingestion when submitting @@ -416,6 +441,35 @@ interface RunService { requestOptions: RequestOptions = RequestOptions.none(), ): HttpResponseFor + /** + * Returns a raw HTTP response for `get /v2/runs/{run_id}/url`, but is otherwise the same as + * [RunService.getUrl]. + */ + @MustBeClosed + fun getUrl(runId: String, params: RunGetUrlParams): HttpResponseFor = + getUrl(runId, params, RequestOptions.none()) + + /** @see getUrl */ + @MustBeClosed + fun getUrl( + runId: String, + params: RunGetUrlParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + getUrl(params.toBuilder().runId(runId).build(), requestOptions) + + /** @see getUrl */ + @MustBeClosed + fun getUrl(params: RunGetUrlParams): HttpResponseFor = + getUrl(params, RequestOptions.none()) + + /** @see getUrl */ + @MustBeClosed + fun getUrl( + params: RunGetUrlParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + /** * Returns a raw HTTP response for `post /runs/batch`, but is otherwise the same as * [RunService.ingestBatch]. diff --git a/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/blocking/RunServiceImpl.kt b/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/blocking/RunServiceImpl.kt index ef532970..518db2af 100644 --- a/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/blocking/RunServiceImpl.kt +++ b/langsmith-java-core/src/main/kotlin/com/langchain/smith/services/blocking/RunServiceImpl.kt @@ -26,6 +26,8 @@ import com.langchain.smith.models.info.InfoListResponse import com.langchain.smith.models.runs.Run import com.langchain.smith.models.runs.RunCreateParams import com.langchain.smith.models.runs.RunCreateResponse +import com.langchain.smith.models.runs.RunGetUrlParams +import com.langchain.smith.models.runs.RunGetUrlResponse import com.langchain.smith.models.runs.RunIngest import com.langchain.smith.models.runs.RunIngestBatchParams import com.langchain.smith.models.runs.RunIngestBatchResponse @@ -188,6 +190,13 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio private val logger = LoggerFactory.getLogger(RunServiceImpl::class.java) } + override fun getUrl( + params: RunGetUrlParams, + requestOptions: RequestOptions, + ): RunGetUrlResponse = + // get /v2/runs/{run_id}/url + withRawResponse().getUrl(params, requestOptions).parse() + override fun ingestBatch( params: RunIngestBatchParams, requestOptions: RequestOptions, @@ -336,6 +345,36 @@ class RunServiceImpl internal constructor(private val clientOptions: ClientOptio } } + private val getUrlHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun getUrl( + params: RunGetUrlParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("runId", params.runId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v2", "runs", params._pathParam(0), "url") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { getUrlHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + private val ingestBatchHandler: Handler = jsonHandler(clientOptions.jsonMapper) diff --git a/langsmith-java-core/src/test/kotlin/com/langchain/smith/models/runs/RunGetUrlParamsTest.kt b/langsmith-java-core/src/test/kotlin/com/langchain/smith/models/runs/RunGetUrlParamsTest.kt new file mode 100644 index 00000000..a15e16a1 --- /dev/null +++ b/langsmith-java-core/src/test/kotlin/com/langchain/smith/models/runs/RunGetUrlParamsTest.kt @@ -0,0 +1,76 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.langchain.smith.models.runs + +import com.langchain.smith.core.http.QueryParams +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class RunGetUrlParamsTest { + + @Test + fun create() { + RunGetUrlParams.builder() + .runId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .projectId("project_id") + .traceId("trace_id") + .startTime("start_time") + .build() + } + + @Test + fun pathParams() { + val params = + RunGetUrlParams.builder() + .runId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .projectId("project_id") + .traceId("trace_id") + .build() + + assertThat(params._pathParam(0)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } + + @Test + fun queryParams() { + val params = + RunGetUrlParams.builder() + .runId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .projectId("project_id") + .traceId("trace_id") + .startTime("start_time") + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("project_id", "project_id") + .put("trace_id", "trace_id") + .put("start_time", "start_time") + .build() + ) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = + RunGetUrlParams.builder() + .runId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .projectId("project_id") + .traceId("trace_id") + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("project_id", "project_id") + .put("trace_id", "trace_id") + .build() + ) + } +} diff --git a/langsmith-java-core/src/test/kotlin/com/langchain/smith/models/runs/RunGetUrlResponseTest.kt b/langsmith-java-core/src/test/kotlin/com/langchain/smith/models/runs/RunGetUrlResponseTest.kt new file mode 100644 index 00000000..e7e305c6 --- /dev/null +++ b/langsmith-java-core/src/test/kotlin/com/langchain/smith/models/runs/RunGetUrlResponseTest.kt @@ -0,0 +1,32 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.langchain.smith.models.runs + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.langchain.smith.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class RunGetUrlResponseTest { + + @Test + fun create() { + val runGetUrlResponse = RunGetUrlResponse.builder().url("url").build() + + assertThat(runGetUrlResponse.url()).contains("url") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val runGetUrlResponse = RunGetUrlResponse.builder().url("url").build() + + val roundtrippedRunGetUrlResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(runGetUrlResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedRunGetUrlResponse).isEqualTo(runGetUrlResponse) + } +} diff --git a/langsmith-java-core/src/test/kotlin/com/langchain/smith/services/async/RunServiceAsyncTest.kt b/langsmith-java-core/src/test/kotlin/com/langchain/smith/services/async/RunServiceAsyncTest.kt index b68ffba4..0ae42e24 100644 --- a/langsmith-java-core/src/test/kotlin/com/langchain/smith/services/async/RunServiceAsyncTest.kt +++ b/langsmith-java-core/src/test/kotlin/com/langchain/smith/services/async/RunServiceAsyncTest.kt @@ -11,6 +11,7 @@ import com.langchain.smith.core.http.Headers import com.langchain.smith.core.http.HttpClient import com.langchain.smith.core.http.HttpRequest import com.langchain.smith.core.http.HttpResponse +import com.langchain.smith.models.runs.RunGetUrlParams import com.langchain.smith.models.runs.RunIngest import com.langchain.smith.models.runs.RunIngestBatchParams import com.langchain.smith.models.runs.RunRetrieveParams @@ -312,6 +313,30 @@ internal class RunServiceAsyncTest { runFuture.get() } + @Disabled("Mock server tests are disabled") + @Test + fun getUrl() { + val client = + LangsmithOkHttpClientAsync.builder() + .apiKey("My API Key") + .tenantId("My Tenant ID") + .build() + val runServiceAsync = client.runs() + + val responseFuture = + runServiceAsync.getUrl( + RunGetUrlParams.builder() + .runId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .projectId("project_id") + .traceId("trace_id") + .startTime("start_time") + .build() + ) + + val response = responseFuture.get() + response.validate() + } + @Disabled("Mock server tests are disabled") @Test fun ingestBatch() { diff --git a/langsmith-java-core/src/test/kotlin/com/langchain/smith/services/blocking/RunServiceTest.kt b/langsmith-java-core/src/test/kotlin/com/langchain/smith/services/blocking/RunServiceTest.kt index 0bcfefc1..b994f06a 100644 --- a/langsmith-java-core/src/test/kotlin/com/langchain/smith/services/blocking/RunServiceTest.kt +++ b/langsmith-java-core/src/test/kotlin/com/langchain/smith/services/blocking/RunServiceTest.kt @@ -25,6 +25,7 @@ import com.langchain.smith.core.http.HttpRequest import com.langchain.smith.core.http.HttpResponse import com.langchain.smith.models.runs.RunAttachment import com.langchain.smith.models.runs.RunCreateParams +import com.langchain.smith.models.runs.RunGetUrlParams import com.langchain.smith.models.runs.RunIngest import com.langchain.smith.models.runs.RunIngestBatchParams import com.langchain.smith.models.runs.RunRetrieveParams @@ -605,6 +606,26 @@ internal class RunServiceTest { private fun testRun(id: String): RunIngest = RunIngest.builder().id(id).traceId(id).dottedOrder("order").name("test").build() + @Disabled("Mock server tests are disabled") + @Test + fun getUrl() { + val client = + LangsmithOkHttpClient.builder().apiKey("My API Key").tenantId("My Tenant ID").build() + val runService = client.runs() + + val response = + runService.getUrl( + RunGetUrlParams.builder() + .runId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .projectId("project_id") + .traceId("trace_id") + .startTime("start_time") + .build() + ) + + response.validate() + } + @Disabled("Mock server tests are disabled") @Test fun ingestBatch() {