@@ -227,18 +227,22 @@ async function findModelById(modelId: string): Promise<ModelInfo | null> {
227227
228228 // Search through all providers
229229 for ( const [ _providerId , provider ] of Object . entries ( data ) ) {
230+ // Skip malformed provider entries
231+ if ( ! provider || typeof provider !== 'object' || ! provider . models ) {
232+ continue ;
233+ }
230234 const model = provider . models [ modelId ] ;
231235 if ( model ) {
232236 return {
233237 id : model . id ,
234238 name : model . name ,
235239 provider : provider . name ,
236- contextLimit : model . limit . context ,
237- outputLimit : model . limit . output ,
238- supportsToolCalls : model . tool_call ,
240+ contextLimit : model . limit ? .context ?? null ,
241+ outputLimit : model . limit ? .output ?? null ,
242+ supportsToolCalls : model . tool_call ?? false ,
239243 cost : {
240- input : model . cost . input ,
241- output : model . cost . output ,
244+ input : model . cost ? .input ?? 0 ,
245+ output : model . cost ? .output ?? 0 ,
242246 } ,
243247 } ;
244248 }
@@ -266,21 +270,29 @@ async function findModelByName(modelName: string): Promise<ModelInfo | null> {
266270
267271 // Search through all providers
268272 for ( const [ _providerId , provider ] of Object . entries ( data ) ) {
273+ // Skip malformed provider entries
274+ if ( ! provider || typeof provider !== 'object' || ! provider . models ) {
275+ continue ;
276+ }
269277 for ( const [ _modelId , model ] of Object . entries ( provider . models ) ) {
278+ // Skip malformed model entries
279+ if ( ! model || typeof model !== 'object' ) {
280+ continue ;
281+ }
270282 if (
271- model . id . toLowerCase ( ) . includes ( lowerName ) ||
272- model . name . toLowerCase ( ) . includes ( lowerName )
283+ ( model . id && model . id . toLowerCase ( ) . includes ( lowerName ) ) ||
284+ ( model . name && model . name . toLowerCase ( ) . includes ( lowerName ) )
273285 ) {
274286 return {
275287 id : model . id ,
276288 name : model . name ,
277289 provider : provider . name ,
278- contextLimit : model . limit . context ,
279- outputLimit : model . limit . output ,
280- supportsToolCalls : model . tool_call ,
290+ contextLimit : model . limit ? .context ?? null ,
291+ outputLimit : model . limit ? .output ?? null ,
292+ supportsToolCalls : model . tool_call ?? false ,
281293 cost : {
282- input : model . cost . input ,
283- output : model . cost . output ,
294+ input : model . cost ? .input ?? 0 ,
295+ output : model . cost ? .output ?? 0 ,
284296 } ,
285297 } ;
286298 }
@@ -297,38 +309,48 @@ async function findModelByName(modelName: string): Promise<ModelInfo | null> {
297309export async function getModelContextLimit (
298310 modelId : string ,
299311) : Promise < number | null > {
300- // Try Ollama fallback first with original model ID (before normalization)
301- // This handles cloud models like gpt-oss:20b-cloud
302- const ollamaLimitOriginal = getOllamaFallbackContextLimit ( modelId ) ;
303- if ( ollamaLimitOriginal ) {
304- return ollamaLimitOriginal ;
305- }
312+ try {
313+ // Try Ollama fallback first with original model ID (before normalization)
314+ // This handles cloud models like gpt-oss:20b-cloud
315+ const ollamaLimitOriginal = getOllamaFallbackContextLimit ( modelId ) ;
316+ if ( ollamaLimitOriginal ) {
317+ return ollamaLimitOriginal ;
318+ }
306319
307- // Strip :cloud or -cloud suffix if present (Ollama cloud models)
308- const normalizedModelId =
309- modelId . endsWith ( ':cloud' ) || modelId . endsWith ( '-cloud' )
310- ? modelId . slice ( 0 , - 6 ) // Remove ":cloud" or "-cloud"
311- : modelId ;
320+ // Strip :cloud or -cloud suffix if present (Ollama cloud models)
321+ const normalizedModelId =
322+ modelId . endsWith ( ':cloud' ) || modelId . endsWith ( '-cloud' )
323+ ? modelId . slice ( 0 , - 6 ) // Remove ":cloud" or "-cloud"
324+ : modelId ;
312325
313- // Try exact ID match first
314- let modelInfo = await findModelById ( normalizedModelId ) ;
326+ // Try exact ID match first
327+ let modelInfo = await findModelById ( normalizedModelId ) ;
315328
316- // Try partial name match if exact match fails
317- if ( ! modelInfo ) {
318- modelInfo = await findModelByName ( normalizedModelId ) ;
319- }
329+ // Try partial name match if exact match fails
330+ if ( ! modelInfo ) {
331+ modelInfo = await findModelByName ( normalizedModelId ) ;
332+ }
320333
321- // If found in models.dev, return that
322- if ( modelInfo ) {
323- return modelInfo . contextLimit ;
324- }
334+ // If found in models.dev, return that
335+ if ( modelInfo ) {
336+ return modelInfo . contextLimit ;
337+ }
325338
326- // Fall back to Ollama model defaults with normalized ID
327- const ollamaLimit = getOllamaFallbackContextLimit ( normalizedModelId ) ;
328- if ( ollamaLimit ) {
329- return ollamaLimit ;
330- }
339+ // Fall back to Ollama model defaults with normalized ID
340+ const ollamaLimit = getOllamaFallbackContextLimit ( normalizedModelId ) ;
341+ if ( ollamaLimit ) {
342+ return ollamaLimit ;
343+ }
331344
332- // No context limit found
333- return null ;
345+ // No context limit found
346+ return null ;
347+ } catch ( error ) {
348+ // Log error but don't crash - just return null
349+ const logger = getLogger ( ) ;
350+ logger . error (
351+ { error : formatError ( error ) , modelId} ,
352+ 'Error getting model context limit' ,
353+ ) ;
354+ return null ;
355+ }
334356}
0 commit comments