File size: 3,713 Bytes
064bfd6 da64cea 064bfd6 da64cea 064bfd6 38d7632 064bfd6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | import type { ModelOption } from './modelOptions.js'
let openRouterModelsCache: ModelOption[] | null = null
let cacheTimestamp: number = 0
let isFetching = false
const CACHE_DURATION = 5 * 60 * 1000 // 5 minutes
export interface OpenRouterModel {
id: string
name: string
description: string
context_length: number
pricing: {
prompt: string
completion: string
}
}
function formatPrice(price: string): string {
const numPrice = parseFloat(price)
if (numPrice === 0) return 'Free'
if (numPrice < 0.000001) return '$<0.000001'
return `$${price}`
}
export async function fetchOpenRouterModels(
apiKey?: string,
): Promise<ModelOption[]> {
// Check cache
if (
openRouterModelsCache !== null &&
Date.now() - cacheTimestamp < CACHE_DURATION
) {
return openRouterModelsCache
}
// Prevent concurrent fetches
if (isFetching) {
return []
}
isFetching = true
// Import auth utilities to get API key
const { getGlobalConfig } = await import('../config.js')
const config = getGlobalConfig()
const key = apiKey || config.openRouterApiKey
if (!key) {
isFetching = false
return []
}
try {
const response = await fetch('https://openrouter.ai/api/v1/models', {
headers: {
Authorization: `Bearer ${key}`,
},
})
if (!response.ok) {
throw new Error(`Failed to fetch models: ${response.statusText}`)
}
const data = await response.json()
const models: OpenRouterModel[] = data.data || []
// Filter out non-model items (routers, aggregators, etc.)
const validModels = models.filter(model => {
const id = model.id.toLowerCase()
const name = model.name.toLowerCase()
// Skip items that are routers, aggregators, or services
const skipPatterns = [
'router',
'aggregator',
'aggregation',
'free.*router',
'routing',
'service',
'platform',
'hub',
]
// Skip if ID or name matches any skip pattern
for (const pattern of skipPatterns) {
if (new RegExp(pattern).test(id) || new RegExp(pattern).test(name)) {
return false
}
}
// Skip items without proper model ID format (should contain provider/model)
if (!id.includes('/') || id.startsWith('router') || id.startsWith('free')) {
return false
}
// Skip items without pricing information
if (!model.pricing || !model.pricing.prompt || !model.pricing.completion) {
return false
}
return true
})
// Convert to ModelOption format
const options: ModelOption[] = validModels.map(model => {
// Truncate description if too long
let description = model.description || model.id
if (description.length > 100) {
description = description.substring(0, 97) + '...'
}
return {
value: model.id,
label: model.name,
description: description,
}
})
// Update cache
openRouterModelsCache = options
cacheTimestamp = Date.now()
return options
} catch (error) {
console.error('Failed to fetch OpenRouter models:', error)
return []
} finally {
isFetching = false
}
}
export function getCachedOpenRouterModels(): ModelOption[] {
return openRouterModelsCache || []
}
export function hasOpenRouterModelsCache(): boolean {
return openRouterModelsCache !== null
}
export function clearOpenRouterModelsCache(): void {
openRouterModelsCache = null
cacheTimestamp = 0
}
// Start background fetch if OpenRouter is configured
export function startOpenRouterModelsFetch(): void {
// Non-blocking background fetch
void fetchOpenRouterModels()
} |