@@ -21,6 +21,8 @@ import {
2121
2222import { Package } from "../../shared/package"
2323
24+ export const BEDROCK_DISCOVERY_TIMEOUT_MS = 30_000
25+
2426// The two AWS SDK packages we use here (`@aws-sdk/client-bedrock` for control-plane discovery
2527// and `@aws-sdk/client-bedrock-runtime` for Converse probes) each ship their own discriminated
2628// `Config` interface where `token`/`credentials` are tagged with package-private branded types.
@@ -152,7 +154,7 @@ const buildInferenceProfileTarget = (summary: InferenceProfileSummary): BedrockD
152154 }
153155}
154156
155- const listInferenceProfiles = async ( client : BedrockClient ) => {
157+ const listInferenceProfiles = async ( client : BedrockClient , abortSignal ?: AbortSignal ) => {
156158 const results : InferenceProfileSummary [ ] = [ ]
157159 let nextToken : string | undefined
158160
@@ -162,6 +164,7 @@ const listInferenceProfiles = async (client: BedrockClient) => {
162164 nextToken,
163165 maxResults : 100 ,
164166 } ) ,
167+ { abortSignal } ,
165168 )
166169
167170 results . push ( ...( response . inferenceProfileSummaries ?? [ ] ) )
@@ -177,42 +180,59 @@ export const discoverBedrockTargets = async (options: ProviderSettings): Promise
177180 }
178181
179182 const client = new BedrockClient ( toBedrockClientConfig ( options ) )
183+ const controller = new AbortController ( )
184+ const timeout = setTimeout ( ( ) => controller . abort ( ) , BEDROCK_DISCOVERY_TIMEOUT_MS )
185+ const timeoutPromise = new Promise < never > ( ( _ , reject ) => {
186+ controller . signal . addEventListener (
187+ "abort" ,
188+ ( ) => reject ( new Error ( `Bedrock discovery timed out after ${ BEDROCK_DISCOVERY_TIMEOUT_MS } ms` ) ) ,
189+ { once : true } ,
190+ )
191+ } )
180192
181- const [ foundationModelsResponse , inferenceProfiles ] = await Promise . all ( [
182- client . send ( new ListFoundationModelsCommand ( { } ) ) ,
183- listInferenceProfiles ( client ) ,
184- ] )
185-
186- const targets = [
187- ...( foundationModelsResponse . modelSummaries ?? [ ] )
188- . map ( ( summary ) => buildFoundationTarget ( summary ) )
189- . filter ( ( target ) : target is BedrockDiscoveredTarget => Boolean ( target ) ) ,
190- ...inferenceProfiles
191- . filter ( ( summary ) => summary . status === "ACTIVE" )
192- . map ( ( summary ) => buildInferenceProfileTarget ( summary ) )
193- . filter ( ( target ) : target is BedrockDiscoveredTarget => Boolean ( target ) ) ,
194- ]
195-
196- const dedupedTargets = Array . from ( new Map ( targets . map ( ( target ) => [ target . id , target ] ) ) . values ( ) )
197-
198- const sortedTargets = dedupedTargets . sort ( ( a , b ) => {
199- const kindOrder = { "foundation-model" : 0 , "system-profile" : 1 , "application-profile" : 2 }
200- const kindCompare = kindOrder [ a . targetKind ] - kindOrder [ b . targetKind ]
201- if ( kindCompare !== 0 ) {
202- return kindCompare
203- }
193+ const discoveryPromise = ( async ( ) => {
194+ const [ foundationModelsResponse , inferenceProfiles ] = await Promise . all ( [
195+ client . send ( new ListFoundationModelsCommand ( { } ) , { abortSignal : controller . signal } ) ,
196+ listInferenceProfiles ( client , controller . signal ) ,
197+ ] )
198+
199+ const targets = [
200+ ...( foundationModelsResponse . modelSummaries ?? [ ] )
201+ . map ( ( summary ) => buildFoundationTarget ( summary ) )
202+ . filter ( ( target ) : target is BedrockDiscoveredTarget => Boolean ( target ) ) ,
203+ ...inferenceProfiles
204+ . filter ( ( summary ) => summary . status === "ACTIVE" )
205+ . map ( ( summary ) => buildInferenceProfileTarget ( summary ) )
206+ . filter ( ( target ) : target is BedrockDiscoveredTarget => Boolean ( target ) ) ,
207+ ]
208+
209+ const dedupedTargets = Array . from ( new Map ( targets . map ( ( target ) => [ target . id , target ] ) ) . values ( ) )
210+
211+ const sortedTargets = dedupedTargets . sort ( ( a , b ) => {
212+ const kindOrder = { "foundation-model" : 0 , "system-profile" : 1 , "application-profile" : 2 }
213+ const kindCompare = kindOrder [ a . targetKind ] - kindOrder [ b . targetKind ]
214+ if ( kindCompare !== 0 ) {
215+ return kindCompare
216+ }
204217
205- if ( a . baseModelId !== b . baseModelId ) {
206- return a . baseModelId . localeCompare ( b . baseModelId )
207- }
218+ if ( a . baseModelId !== b . baseModelId ) {
219+ return a . baseModelId . localeCompare ( b . baseModelId )
220+ }
208221
209- return a . label . localeCompare ( b . label )
210- } )
222+ return a . label . localeCompare ( b . label )
223+ } )
211224
212- // AWS often returns a single inference profile id for models that support both 128K
213- // and 1M context windows. Expand those into two dropdown entries so users can pick
214- // the context tier explicitly; the `:1m` suffix is round-tripped through the runtime.
215- return expandBedrockTargetsWith1MVariants ( sortedTargets )
225+ // AWS often returns a single inference profile id for models that support both 128K
226+ // and 1M context windows. Expand those into two dropdown entries so users can pick
227+ // the context tier explicitly; the `:1m` suffix is round-tripped through the runtime.
228+ return expandBedrockTargetsWith1MVariants ( sortedTargets )
229+ } ) ( )
230+
231+ try {
232+ return await Promise . race ( [ discoveryPromise , timeoutPromise ] )
233+ } finally {
234+ clearTimeout ( timeout )
235+ }
216236}
217237
218238/**
0 commit comments