@@ -19,8 +19,10 @@ import com.langchain.smith.core.http.HttpResponse.Handler
1919import com.langchain.smith.core.http.HttpResponseFor
2020import com.langchain.smith.core.http.json
2121import com.langchain.smith.core.http.parseable
22+ import com.langchain.smith.core.http.zstd
2223import com.langchain.smith.core.prepareAsync
2324import com.langchain.smith.errors.NotFoundException
25+ import com.langchain.smith.models.info.InfoListResponse
2426import com.langchain.smith.models.runs.RunCreateParams
2527import com.langchain.smith.models.runs.RunCreateResponse
2628import com.langchain.smith.models.runs.RunIngestBatchParams
@@ -35,9 +37,10 @@ import com.langchain.smith.models.runs.RunUpdate2Params
3537import com.langchain.smith.models.runs.RunUpdate2Response
3638import com.langchain.smith.models.runs.RunUpdateParams
3739import com.langchain.smith.models.runs.RunUpdateResponse
38- import com.langchain.smith.services.async.annotationqueues.InfoServiceAsyncImpl
3940import com.langchain.smith.services.async.runs.RuleServiceAsync
4041import com.langchain.smith.services.async.runs.RuleServiceAsyncImpl
42+ import com.langchain.smith.services.isZstdCompressionEnabled
43+ import com.langchain.smith.services.shouldDefaultRunCompressionEnabled
4144import java.util.concurrent.CompletableFuture
4245import java.util.concurrent.CompletionException
4346import java.util.concurrent.atomic.AtomicBoolean
@@ -48,7 +51,11 @@ import org.slf4j.LoggerFactory
4851class RunServiceAsyncImpl internal constructor(private val clientOptions : ClientOptions ) :
4952 RunServiceAsync {
5053
51- private val withRawResponse: WithRawResponseImpl by lazy { WithRawResponseImpl (clientOptions) }
54+ private val serverInfo: CompletableFuture <InfoListResponse ?> by lazy { fetchServerInfo() }
55+
56+ private val withRawResponse: WithRawResponseImpl by lazy {
57+ WithRawResponseImpl (clientOptions = clientOptions, getServerInfo = { serverInfo })
58+ }
5259
5360 private val rules: RuleServiceAsync by lazy { RuleServiceAsyncImpl (clientOptions) }
5461 private val multipartDisabled = AtomicBoolean (false )
@@ -110,17 +117,36 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client
110117 null
111118 }
112119
113- private fun fetchAutoBatchIngestLimits () =
120+ private fun fetchServerInfo (): CompletableFuture <InfoListResponse ?> =
121+ try {
122+ InfoServiceAsyncImpl (clientOptions).list().handle { info, throwable ->
123+ if (throwable != null ) {
124+ logger.warn(
125+ " Failed to fetch LangSmith server info; using default batch limits and compression settings" ,
126+ throwable,
127+ )
128+ null
129+ } else {
130+ info
131+ }
132+ }
133+ } catch (e: Exception ) {
134+ logger.warn(
135+ " Failed to fetch LangSmith server info; using default batch limits and compression settings" ,
136+ e,
137+ )
138+ CompletableFuture .completedFuture(null )
139+ }
140+
141+ private fun fetchAutoBatchIngestLimits (): AutoBatchIngestLimits =
114142 try {
115- InfoServiceAsyncImpl (clientOptions)
116- .list()
117- .get()
118- .batchIngestConfig()
119- .getOrNull()
120- .toAutoBatchIngestLimits()
143+ serverInfo.get()?.batchIngestConfig()?.getOrNull().toAutoBatchIngestLimits()
144+ } catch (e: InterruptedException ) {
145+ Thread .currentThread().interrupt()
146+ AutoBatchIngestLimits ()
121147 } catch (e: Exception ) {
122148 logger.warn(
123- " Failed to fetch LangSmith batch ingest config ; using default batch limits" ,
149+ " Failed to fetch LangSmith server info ; using default batch limits and compression settings " ,
124150 e,
125151 )
126152 AutoBatchIngestLimits ()
@@ -226,12 +252,37 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client
226252 CompletableFuture <T >().also { it.completeExceptionally(throwable) }
227253 }
228254
229- class WithRawResponseImpl internal constructor(private val clientOptions : ClientOptions ) :
230- RunServiceAsync .WithRawResponse {
255+ class WithRawResponseImpl
256+ internal constructor (
257+ private val clientOptions: ClientOptions ,
258+ private val getServerInfo: () -> CompletableFuture <InfoListResponse ?> = {
259+ CompletableFuture .completedFuture(null )
260+ },
261+ ) : RunServiceAsync .WithRawResponse {
231262
232263 private val errorHandler: Handler <HttpResponse > =
233264 errorHandler(errorBodyHandler(clientOptions.jsonMapper))
234265
266+ private val zstdCompressionEnabled: CompletableFuture <Boolean > by lazy {
267+ fetchZstdCompressionEnabled()
268+ }
269+
270+ private fun fetchZstdCompressionEnabled (): CompletableFuture <Boolean > {
271+ if (! shouldDefaultRunCompressionEnabled()) {
272+ return CompletableFuture .completedFuture(false )
273+ }
274+ return try {
275+ getServerInfo()
276+ .thenApply { info ->
277+ info?.let (::isZstdCompressionEnabled)
278+ ? : shouldDefaultRunCompressionEnabled()
279+ }
280+ .exceptionally { shouldDefaultRunCompressionEnabled() }
281+ } catch (_: Exception ) {
282+ CompletableFuture .completedFuture(shouldDefaultRunCompressionEnabled())
283+ }
284+ }
285+
235286 private val rules: RuleServiceAsync .WithRawResponse by lazy {
236287 RuleServiceAsyncImpl .WithRawResponseImpl (clientOptions)
237288 }
@@ -384,16 +435,25 @@ class RunServiceAsyncImpl internal constructor(private val clientOptions: Client
384435 // back to legacy JSON batch ingest for this batch only.
385436 return CompletableFuture .completedFuture(false )
386437 }
387- val request =
388- HttpRequest .builder()
389- .method(HttpMethod .POST )
390- .baseUrl(clientOptions.baseUrl())
391- .addPathSegments(" runs" , " multipart" )
392- .body(body)
393- .build()
394- .prepareAsync(clientOptions, params)
395438 val requestOptions = requestOptions.applyDefaults(RequestOptions .from(clientOptions))
396- return request
439+ return zstdCompressionEnabled
440+ .thenCompose { zstdCompressionEnabled ->
441+ val requestBuilder =
442+ HttpRequest .builder()
443+ .method(HttpMethod .POST )
444+ .baseUrl(clientOptions.baseUrl())
445+ .addPathSegments(" runs" , " multipart" )
446+ if (zstdCompressionEnabled) {
447+ requestBuilder.putHeader(" Content-Encoding" , " zstd" ).body(zstd(body))
448+ } else {
449+ requestBuilder.body(body)
450+ }
451+ logger.debug(
452+ " Sending LangSmith run batch to multipart ingest endpoint (zstd compression: {})" ,
453+ if (zstdCompressionEnabled) " enabled" else " disabled" ,
454+ )
455+ requestBuilder.build().prepareAsync(clientOptions, params)
456+ }
397457 .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) }
398458 .thenApply { response ->
399459 errorHandler.handle(response).use {}
0 commit comments