File size: 4,271 Bytes
064bfd6 da64cea 064bfd6 da64cea 064bfd6 38d7632 064bfd6 78e2eb9 15aad85 78e2eb9 15aad85 78e2eb9 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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | 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 getFirstFreeModel(): ModelOption | null {
if (!openRouterModelsCache || openRouterModelsCache.length === 0) {
return null
}
return openRouterModelsCache.find(model => {
// Check if this model is free by looking at the model name
// Models with ":free" or "(free)" in the name are free
const label = (model.label || '').toLowerCase()
const value = (model.value || '').toLowerCase()
return label.includes(':free') || label.includes('(free)') || value.includes(':free') || value.includes('(free)')
}) || 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()
} |