chore(vendor): import pi 1.3.0
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
1 parent
719370c8f7
commit
6081e3e325
1743 files changed
+205459
-131704
No files matched your search
@@ -0,0 +1,105 @@
|
||||
import { type OpenRouterModel, openRouterCost } from "../src/providers/openrouter-api.ts";
|
||||
import type { ClassifierModel, ImageModel, Model } from "../src/types.ts";
|
||||
import { getOpenRouterThinkingLevelMap, type OpenRouterReasoningMetadata } from "./openrouter-reasoning-options.ts";
|
||||
|
||||
export interface OpenRouterModelListItem extends OpenRouterModel {
|
||||
name: string;
|
||||
supported_parameters?: string[];
|
||||
architecture?: { modality?: string; input_modalities?: string[]; output_modalities?: string[] };
|
||||
reasoning?: OpenRouterReasoningMetadata;
|
||||
}
|
||||
|
||||
export interface OpenRouterCatalog {
|
||||
chat: Model<"anthropic-messages" | "openai-completions">[];
|
||||
images: ImageModel<"openrouter-images">[];
|
||||
classifiers: ClassifierModel<"typesafe-system-one">[];
|
||||
}
|
||||
|
||||
function modalities(values: string[] | undefined): ("text" | "image")[] {
|
||||
return Array.from(
|
||||
new Set((values ?? []).filter((value): value is "text" | "image" => value === "text" || value === "image")),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the OpenRouter catalog from the default listing and the
|
||||
* `output_modalities=image` and `output_modalities=decisions` listings. The
|
||||
* default listing omits image-only and decision models, so those come from
|
||||
* the other listings. An upstream model may appear in several results; it then
|
||||
* gets separate entries per operation.
|
||||
*/
|
||||
export function buildOpenRouterCatalog(
|
||||
listed: readonly OpenRouterModelListItem[],
|
||||
imageListed: readonly OpenRouterModelListItem[],
|
||||
decisionListed: 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: openRouterCost(model.pricing),
|
||||
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: openRouterCost(model.pricing),
|
||||
});
|
||||
}
|
||||
|
||||
// Decision models such as TypeSafe's Jev are served through OpenRouter's
|
||||
// TypeSafe-compatible System One endpoint.
|
||||
const classifiers: OpenRouterCatalog["classifiers"] = [];
|
||||
for (const model of decisionListed) {
|
||||
if (classifiers.some((entry) => entry.id === model.id)) continue;
|
||||
if (!model.architecture?.output_modalities?.includes("decisions")) continue;
|
||||
const input = modalities(model.architecture.input_modalities);
|
||||
classifiers.push({
|
||||
type: "classifier",
|
||||
id: model.id,
|
||||
name: model.name,
|
||||
api: "typesafe-system-one",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
input: input.length > 0 ? input : ["text"],
|
||||
cost: openRouterCost(model.pricing),
|
||||
contextWindow: model.top_provider?.context_length || model.context_length || 4096,
|
||||
});
|
||||
}
|
||||
|
||||
return { chat, images, classifiers };
|
||||
}
|
||||
Reference in new issue
Block a user