mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
107 lines
3.6 KiB
TypeScript
107 lines
3.6 KiB
TypeScript
import type { ImageModel, Model, ModelCost } from "../src/types.ts";
|
|
import { getOpenRouterThinkingLevelMap, type OpenRouterReasoningMetadata } from "./openrouter-reasoning-options.ts";
|
|
|
|
export interface OpenRouterModelListItem {
|
|
id: string;
|
|
name: string;
|
|
supported_parameters?: string[];
|
|
architecture?: { modality?: string; input_modalities?: string[]; output_modalities?: string[] };
|
|
pricing?: {
|
|
prompt?: string;
|
|
completion?: string;
|
|
input_cache_read?: string;
|
|
input_cache_write?: string;
|
|
};
|
|
top_provider?: {
|
|
context_length?: number;
|
|
max_completion_tokens?: number;
|
|
};
|
|
context_length?: number;
|
|
reasoning?: OpenRouterReasoningMetadata;
|
|
}
|
|
|
|
export interface OpenRouterCatalog {
|
|
chat: Model<"anthropic-messages" | "openai-completions">[];
|
|
images: ImageModel<"openrouter-images">[];
|
|
}
|
|
|
|
function roundCost(value: number): number {
|
|
return Number(value.toFixed(6));
|
|
}
|
|
|
|
function modalities(values: string[] | undefined): ("text" | "image")[] {
|
|
return Array.from(
|
|
new Set((values ?? []).filter((value): value is "text" | "image" => value === "text" || value === "image")),
|
|
);
|
|
}
|
|
|
|
function cost(model: OpenRouterModelListItem): ModelCost {
|
|
// Convert pricing from $/token to $/million tokens
|
|
return {
|
|
input: roundCost(parseFloat(model.pricing?.prompt || "0") * 1_000_000),
|
|
output: roundCost(parseFloat(model.pricing?.completion || "0") * 1_000_000),
|
|
cacheRead: roundCost(parseFloat(model.pricing?.input_cache_read || "0") * 1_000_000),
|
|
cacheWrite: roundCost(parseFloat(model.pricing?.input_cache_write || "0") * 1_000_000),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Build the OpenRouter catalog from the default listing and the
|
|
* `output_modalities=image` listing. The default listing omits image-only
|
|
* models, so image models come from the second one. An upstream model may
|
|
* appear in both results; it then gets separate chat and image entries.
|
|
*/
|
|
export function buildOpenRouterCatalog(
|
|
listed: readonly OpenRouterModelListItem[],
|
|
imageListed: readonly OpenRouterModelListItem[],
|
|
): OpenRouterCatalog {
|
|
const chat: OpenRouterCatalog["chat"] = [];
|
|
|
|
for (const model of listed) {
|
|
// Only include models that support tools
|
|
if (!model.supported_parameters?.includes("tools")) continue;
|
|
// Parse input modalities
|
|
const input: ("text" | "image")[] = ["text"];
|
|
if (model.architecture?.modality?.includes("image")) {
|
|
input.push("image");
|
|
}
|
|
|
|
const thinkingLevelMap = getOpenRouterThinkingLevelMap(model.reasoning);
|
|
const useAnthropicMessages = /^anthropic\//.test(model.id) && !model.id.endsWith(":batch");
|
|
chat.push({
|
|
type: "chat",
|
|
id: model.id,
|
|
name: model.name,
|
|
api: useAnthropicMessages ? "anthropic-messages" : "openai-completions",
|
|
baseUrl: useAnthropicMessages ? "https://openrouter.ai/api" : "https://openrouter.ai/api/v1",
|
|
provider: "openrouter",
|
|
reasoning: model.supported_parameters?.includes("reasoning") || false,
|
|
...(thinkingLevelMap && { thinkingLevelMap }),
|
|
input,
|
|
cost: cost(model),
|
|
contextWindow: model.top_provider?.context_length || model.context_length || 4096,
|
|
maxTokens: model.top_provider?.max_completion_tokens || 4096,
|
|
});
|
|
}
|
|
|
|
const images: OpenRouterCatalog["images"] = [];
|
|
for (const model of imageListed) {
|
|
if (images.some((entry) => entry.id === model.id)) continue;
|
|
const output = modalities(model.architecture?.output_modalities);
|
|
if (!output.includes("image")) continue;
|
|
const input = modalities(model.architecture?.input_modalities);
|
|
images.push({
|
|
type: "image",
|
|
id: model.id,
|
|
name: model.name,
|
|
api: "openrouter-images",
|
|
provider: "openrouter",
|
|
baseUrl: "https://openrouter.ai/api/v1",
|
|
input: input.length > 0 ? input : ["text"],
|
|
output,
|
|
cost: cost(model),
|
|
});
|
|
}
|
|
|
|
return { chat, images };
|
|
}
|