( model: OpenRouterModel, existing: ExistingModel | undefined, baseModel?: string, )
| 158 | } |
| 159 | |
| 160 | export function buildOpenRouterModel( |
| 161 | model: OpenRouterModel, |
| 162 | existing: ExistingModel | undefined, |
| 163 | baseModel?: string, |
| 164 | ): SyncedModel { |
| 165 | const params = new Set(model.supported_parameters); |
| 166 | const name = model.name.replace(/^[^:]+:\s+/, ""); |
| 167 | const input = modalities(model.architecture.input_modalities, ["text"]); |
| 168 | const output = modalities(model.architecture.output_modalities, ["text"]); |
| 169 | const prompt = price(model.pricing.prompt); |
| 170 | const completion = price(model.pricing.completion); |
| 171 | const reasoning = params.has("reasoning") || params.has("include_reasoning"); |
| 172 | const reasoning_options = existing?.reasoning_options?.length |
| 173 | ? existing.reasoning_options |
| 174 | : openRouterReasoningOptions(model.reasoning) ?? existing?.reasoning_options; |
| 175 | const context = model.top_provider.context_length ?? model.context_length; |
| 176 | const family = inferFamily(model, name); |
| 177 | const releaseDate = dateFromTimestamp(model.created); |
| 178 | const familyValue = existing?.family === "o" && family !== "o" |
| 179 | ? family |
| 180 | : (existing?.family ?? family); |
| 181 | const attachment = input.some((value) => value !== "text"); |
| 182 | const toolCall = params.has("tools") || params.has("tool_choice"); |
| 183 | const structuredOutput = params.has("structured_outputs"); |
| 184 | const knowledge = model.knowledge_cutoff?.slice(0, 10) ?? existing?.knowledge; |
| 185 | const openWeights = Boolean(model.hugging_face_id); |
| 186 | const cost = prompt !== undefined && completion !== undefined |
| 187 | ? { |
| 188 | input: prompt, |
| 189 | output: completion, |
| 190 | reasoning: reasoning ? price(model.pricing.internal_reasoning) : undefined, |
| 191 | cache_read: price(model.pricing.input_cache_read), |
| 192 | cache_write: price(model.pricing.input_cache_write), |
| 193 | tiers: existing?.cost?.tiers, |
| 194 | } |
| 195 | : existing?.cost; |
| 196 | const limit = { |
| 197 | context, |
| 198 | input: existing?.limit?.input, |
| 199 | output: model.top_provider.max_completion_tokens ?? existing?.limit?.output ?? context, |
| 200 | }; |
| 201 | const canonical = existing?.base_model ?? baseModel ?? resolveCanonicalBaseModel(model.id); |
| 202 | |
| 203 | if (canonical !== undefined) { |
| 204 | return factorBaseModel( |
| 205 | canonical, |
| 206 | { |
| 207 | name: baseModel !== undefined || model.id.endsWith(":free") ? name : undefined, |
| 208 | description: existing?.description ?? describeModel({ |
| 209 | id: model.id, |
| 210 | name, |
| 211 | family: familyValue, |
| 212 | reasoning, |
| 213 | tool_call: toolCall, |
| 214 | structured_output: structuredOutput, |
| 215 | open_weights: openWeights, |
| 216 | limit, |
| 217 | modalities: { input, output }, |
no test coverage detected