@@ -28,6 +28,7 @@ const {
2828const { isWsl } = require ( "../platform" ) ;
2929const httpProbe = require ( "../adapters/http/probe" ) ;
3030const authConfigModule = require ( "../adapters/http/auth-config" ) ;
31+ const trace = require ( "../trace" ) ;
3132const {
3233 getHostDockerInternalProbeFailure,
3334 isHijackedDockerInternalUrl,
@@ -44,6 +45,7 @@ const {
4445} = require ( "./probe-retry" ) ;
4546const { probeAnthropicEndpoint } = require ( "./probe-anthropic" ) ;
4647const {
48+ buildValidationProbeTimingProfile,
4749 getValidationProbeCurlArgs,
4850 getDeepSeekV4ProValidationProbeCurlArgs,
4951 getKimiK26ValidationProbeCurlArgs,
@@ -232,6 +234,55 @@ function getProbeAuthMode(_provider) {
232234 return undefined ;
233235}
234236
237+ function getProbeTimingOptions ( options = { } ) {
238+ const timingOptions = { } ;
239+ if ( typeof options . isWsl === "boolean" ) {
240+ timingOptions . isWsl = options . isWsl ;
241+ }
242+ if ( options . validationTiming ) {
243+ timingOptions . validationTiming = options . validationTiming ;
244+ }
245+ return Object . keys ( timingOptions ) . length > 0 ? timingOptions : undefined ;
246+ }
247+
248+ function calibrateOpenAiLikeValidationTiming ( baseUrl , options = { } ) {
249+ return trace . withTraceSpan ( "nemoclaw.inference.validation_timeout_calibration" , { } , ( ) => {
250+ const url = `${ baseUrl } /models` ;
251+ const args = [
252+ "-sS" ,
253+ ...buildResolvePinArgs ( url , options . pinnedAddresses ) ,
254+ "--connect-timeout" ,
255+ "3" ,
256+ "--max-time" ,
257+ "5" ,
258+ url ,
259+ ] ;
260+ const startedAtMs = Date . now ( ) ;
261+ const result = runCurlProbe ( args , {
262+ timeoutMs : getProbeProcessTimeoutMs ( args ) ,
263+ pinnedAddresses : options . pinnedAddresses ,
264+ } ) ;
265+ const durationMs = Date . now ( ) - startedAtMs ;
266+ const calibration =
267+ result . curlStatus === 0 && result . httpStatus > 0
268+ ? { ok : true , durationMs }
269+ : { ok : false , reason : result . message } ;
270+ const profile = buildValidationProbeTimingProfile ( {
271+ ...( typeof options . isWsl === "boolean" ? { isWsl : options . isWsl } : { } ) ,
272+ calibration,
273+ } ) ;
274+ trace . addTraceEvent ( "validation_timeout_profile" , {
275+ calibration_curl_status : result . curlStatus ,
276+ calibration_http_status : result . httpStatus ,
277+ connect_timeout_seconds : profile . connectTimeoutSeconds ,
278+ max_time_seconds : profile . maxTimeSeconds ,
279+ observed_ms : profile . observedMs ?? null ,
280+ source : profile . source ,
281+ } ) ;
282+ return profile ;
283+ } ) ;
284+ }
285+
235286// ── Responses API probe ──────────────────────────────────────────
236287
237288function probeResponsesToolCalling ( endpointUrl , model , apiKey , options = { } ) {
@@ -243,7 +294,7 @@ function probeResponsesToolCalling(endpointUrl, model, apiKey, options = {}) {
243294 [
244295 "-sS" ,
245296 ...buildResolvePinArgs ( `${ baseUrl } /responses` , options . pinnedAddresses ) ,
246- ...getValidationProbeCurlArgs ( ) ,
297+ ...getValidationProbeCurlArgs ( getProbeTimingOptions ( options ) ) ,
247298 "-H" ,
248299 "Content-Type: application/json" ,
249300 ...authConfig . args ,
@@ -302,7 +353,9 @@ function probeChatCompletionsToolCalling(endpointUrl, model, apiKey, options = {
302353 let authConfig ;
303354 try {
304355 authConfig = buildOpenAiLikeAuthConfig ( apiKey , options ) ;
305- const timingArgs = options . timingArgs ?? getChatCompletionsProbeTimingArgs ( model ) ;
356+ const timingArgs =
357+ options . timingArgs ??
358+ getChatCompletionsProbeTimingArgs ( model , getProbeTimingOptions ( options ) ) ;
306359 const args = [
307360 "-sS" ,
308361 ...buildResolvePinArgs ( `${ baseUrl } /chat/completions` , options . pinnedAddresses ) ,
@@ -483,9 +536,21 @@ export function getChatCompletionsProbeCurlArgs(opts: {
483536 url : string ;
484537 isWsl ?: boolean ;
485538 pinnedAddresses ?: readonly string [ ] ;
539+ validationTiming ?: unknown ;
486540} ) {
487- const { credentialArgs, authHeader, model, url, isWsl : isWslOverride , pinnedAddresses } = opts ;
488- const platformOptions = typeof isWslOverride === "boolean" ? { isWsl : isWslOverride } : undefined ;
541+ const {
542+ credentialArgs,
543+ authHeader,
544+ model,
545+ url,
546+ isWsl : isWslOverride ,
547+ pinnedAddresses,
548+ validationTiming,
549+ } = opts ;
550+ const platformOptions = getProbeTimingOptions ( {
551+ ...( typeof isWslOverride === "boolean" ? { isWsl : isWslOverride } : { } ) ,
552+ ...( validationTiming ? { validationTiming } : { } ) ,
553+ } ) ;
489554 const timingArgs = getChatCompletionsProbeTimingArgs ( model , platformOptions ) ;
490555 const credSlice = credentialArgs ?? authHeader ?? [ ] ;
491556 return [
@@ -508,13 +573,15 @@ function runChatCompletionsProbe({
508573 isWsl : isWslOverride ,
509574 trustedConfigFiles,
510575 pinnedAddresses,
576+ validationTiming,
511577} ) {
512578 const args = getChatCompletionsProbeCurlArgs ( {
513579 credentialArgs,
514580 model,
515581 url,
516582 isWsl : isWslOverride ,
517583 pinnedAddresses,
584+ validationTiming,
518585 } ) ;
519586 const probeOpts = { timeoutMs : getProbeProcessTimeoutMs ( args ) , pinnedAddresses } ;
520587 if ( trustedConfigFiles && trustedConfigFiles . length > 0 ) {
@@ -538,7 +605,7 @@ function runDoubledTimeoutChatCompletionsRetry({
538605 baseUrl,
539606 authConfig,
540607} ) {
541- const platformOptions = typeof options . isWsl === "boolean" ? { isWsl : options . isWsl } : undefined ;
608+ const platformOptions = getProbeTimingOptions ( options ) ;
542609 const baseArgs = getChatCompletionsProbeTimingArgs ( model , platformOptions ) ;
543610 const doubledArgs = baseArgs . map ( ( arg ) => ( / ^ \d + $ / . test ( arg ) ? String ( Number ( arg ) * 2 ) : arg ) ) ;
544611 const buildRetryArgs = ( ) => [
@@ -651,6 +718,14 @@ function probeOpenAiLikeEndpoint(endpointUrl, model, apiKey, options = {}) {
651718 }
652719
653720 const baseUrl = String ( endpointUrl ) . replace ( / \/ + $ / , "" ) ;
721+ const validationTiming =
722+ options . validationTiming ??
723+ ( options . calibrateTimeouts === true
724+ ? calibrateOpenAiLikeValidationTiming ( baseUrl , options )
725+ : undefined ) ;
726+ if ( validationTiming ) {
727+ options = { ...options , validationTiming } ;
728+ }
654729 // Pin every probe curl to the SSRF-preflight-validated address(es) the caller
655730 // captured, so a second DNS lookup here cannot rebind the hostname to a
656731 // private/internal address after the public preflight (TOCTOU — cv, #6293).
@@ -667,6 +742,7 @@ function probeOpenAiLikeEndpoint(endpointUrl, model, apiKey, options = {}) {
667742 probeResponsesToolCalling ( endpointUrl , model , apiKey , {
668743 authMode : options . authMode ,
669744 pinnedAddresses,
745+ validationTiming,
670746 } ) ,
671747 }
672748 : {
@@ -677,7 +753,7 @@ function probeOpenAiLikeEndpoint(endpointUrl, model, apiKey, options = {}) {
677753 [
678754 "-sS" ,
679755 ...buildResolvePinArgs ( `${ baseUrl } /responses` , pinnedAddresses ) ,
680- ...getValidationProbeCurlArgs ( ) ,
756+ ...getValidationProbeCurlArgs ( getProbeTimingOptions ( options ) ) ,
681757 "-H" ,
682758 "Content-Type: application/json" ,
683759 ...authConfig . args ,
@@ -700,6 +776,7 @@ function probeOpenAiLikeEndpoint(endpointUrl, model, apiKey, options = {}) {
700776 ? probeChatCompletionsToolCalling ( endpointUrl , model , apiKey , {
701777 authMode : options . authMode ,
702778 pinnedAddresses,
779+ validationTiming,
703780 } )
704781 : runChatCompletionsProbe ( {
705782 credentialArgs : authConfig . args ,
@@ -708,6 +785,7 @@ function probeOpenAiLikeEndpoint(endpointUrl, model, apiKey, options = {}) {
708785 isWsl : options . isWsl ,
709786 trustedConfigFiles : authConfig . trustedConfigFiles ,
710787 pinnedAddresses,
788+ validationTiming,
711789 } ) ,
712790 } ;
713791
@@ -741,7 +819,7 @@ function probeOpenAiLikeEndpoint(endpointUrl, model, apiKey, options = {}) {
741819 [
742820 "-sS" ,
743821 ...buildResolvePinArgs ( `${ baseUrl } /responses` , pinnedAddresses ) ,
744- ...getValidationProbeCurlArgs ( ) ,
822+ ...getValidationProbeCurlArgs ( getProbeTimingOptions ( options ) ) ,
745823 "-H" ,
746824 "Content-Type: application/json" ,
747825 ...authConfig . args ,
0 commit comments