SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
68d7816 verified
Raw
History Blame Contribute Delete
12 kB
import type { CatalogModelDefinition } from '#/llm/provider-catalog';
export type ModelsDevWire =
| 'anthropic'
| 'openai'
| 'openai_responses'
| 'google-genai'
| 'google-vertex'
| 'kimi';
export interface ModelsDevModelEntry {
readonly id?: string;
readonly name?: string;
readonly family?: string;
readonly limit?: { readonly context?: number; readonly input?: number; readonly output?: number };
readonly tool_call?: boolean;
readonly dynamically_loaded_tools?: boolean;
readonly reasoning?: boolean;
readonly reasoning_options?: readonly ModelsDevReasoningOption[];
readonly status?: string;
readonly provider?: ModelsDevModelProviderOverride;
readonly interleaved?: boolean | { readonly field?: string };
readonly modalities?: {
readonly input?: readonly string[];
readonly output?: readonly string[];
};
}
export interface ModelsDevReasoningOption {
readonly type?: string;
readonly values?: unknown;
}
export interface ModelsDevModelProviderOverride {
readonly npm?: string;
readonly api?: string;
}
export interface ModelsDevProviderEntry {
readonly id?: string;
readonly api?: string;
readonly npm?: string;
readonly type?: string;
readonly models?: Record<string, ModelsDevModelEntry>;
}
export type ModelsDevImportInvalidReason =
| 'unknown-explicit-type'
| 'proprietary-sdk'
| 'empty-base-url'
| 'placeholder-base-url';
export type ModelsDevImportResolution =
| {
readonly kind: 'ok';
readonly wire: ModelsDevWire;
readonly guessed: boolean;
readonly baseUrl?: string;
}
| { readonly kind: 'needs-base-url'; readonly wire: ModelsDevWire; readonly guessed: boolean }
| { readonly kind: 'invalid'; readonly reason: ModelsDevImportInvalidReason };
const KNOWN_WIRES = [
'anthropic',
'openai',
'openai_responses',
'google-genai',
'google-vertex',
'kimi',
] as const satisfies readonly ModelsDevWire[];
function isModelsDevWire(value: unknown): value is ModelsDevWire {
return typeof value === 'string' && (KNOWN_WIRES as readonly string[]).includes(value);
}
function hasEmbeddingMarker(value: string | undefined): boolean {
if (value === undefined) return false;
const lower = value.toLowerCase();
return lower.includes('embedding') || /(?:^|[-_/])embed(?:$|[-_/])/.test(lower);
}
function isUsableChatModel(model: ModelsDevModelEntry): boolean {
const outputModalities = model.modalities?.output;
if (outputModalities !== undefined && !outputModalities.includes('text')) return false;
if (model.status === 'deprecated' || model.status === 'alpha') return false;
return (
!hasEmbeddingMarker(model.family) &&
!hasEmbeddingMarker(model.id) &&
!hasEmbeddingMarker(model.name)
);
}
export function resolveModelsDevImport(
entry: ModelsDevProviderEntry,
userBaseUrl?: string,
): ModelsDevImportResolution {
const wire = resolveModelsDevWire(entry);
if (wire === undefined) {
return {
kind: 'invalid',
reason:
typeof entry.type === 'string' && entry.type.length > 0
? 'unknown-explicit-type'
: 'proprietary-sdk',
};
}
const guessed = inferDeclaredWire(entry) === undefined;
if (userBaseUrl !== undefined) {
const trimmed = userBaseUrl.trim();
if (trimmed.length === 0) return { kind: 'invalid', reason: 'empty-base-url' };
if (trimmed.includes('${')) return { kind: 'invalid', reason: 'placeholder-base-url' };
return { kind: 'ok', wire, guessed, baseUrl: adaptBaseUrlForWire(trimmed, wire) };
}
const modelsDevUrl = modelsDevBaseUrl(entry, wire);
if (modelsDevUrl !== undefined) return { kind: 'ok', wire, guessed, baseUrl: modelsDevUrl };
if (modelsDevEndpointRequired(entry, wire)) return { kind: 'needs-base-url', wire, guessed };
return { kind: 'ok', wire, guessed };
}
function resolveModelsDevWire(entry: ModelsDevProviderEntry): ModelsDevWire | undefined {
if (isModelsDevWire(entry.type)) return entry.type;
if (typeof entry.type === 'string' && entry.type.length > 0) return undefined;
const declared = inferDeclaredWire(entry);
if (declared !== undefined) return declared;
const npm = (entry.npm ?? '').toLowerCase();
if (npm.includes('amazon-bedrock') || npm.includes('cohere')) return undefined;
return 'openai';
}
function inferDeclaredWire(entry: ModelsDevProviderEntry): ModelsDevWire | undefined {
if (isModelsDevWire(entry.type)) return entry.type;
const npm = (entry.npm ?? '').toLowerCase();
const id = (entry.id ?? '').toLowerCase();
if (npm.includes('anthropic') || id.includes('anthropic') || id.includes('claude')) {
return 'anthropic';
}
if (id.includes('vertex')) return 'google-vertex';
if (npm.includes('google') || id.includes('google') || id.includes('gemini')) {
return 'google-genai';
}
if (npm.includes('openai') || id.includes('openai')) return 'openai';
return undefined;
}
function modelsDevBaseUrl(entry: ModelsDevProviderEntry, wire: ModelsDevWire): string | undefined {
const api = entry.api;
if (typeof api !== 'string' || api.length === 0 || api.includes('${')) return undefined;
return adaptBaseUrlForWire(api, wire);
}
function adaptBaseUrlForWire(baseUrl: string, wire: ModelsDevWire): string {
return wire === 'anthropic' ? baseUrl.replace(/\/v1\/?$/, '') : baseUrl;
}
function modelsDevEndpointRequired(entry: ModelsDevProviderEntry, wire: ModelsDevWire): boolean {
if (typeof entry.api === 'string' && entry.api.length > 0) return true;
const npm = (entry.npm ?? '').toLowerCase();
if (wire === 'openai' || wire === 'openai_responses') return npm !== '@ai-sdk/openai';
if (wire === 'anthropic') return npm !== '@ai-sdk/anthropic';
return false;
}
function normalizeModelsDevModel(
providerId: string,
model: ModelsDevModelEntry,
): CatalogModelDefinition | undefined {
if (typeof model.id !== 'string' || model.id.length === 0) return undefined;
const context = model.limit?.context;
if (typeof context !== 'number' || !Number.isInteger(context) || context <= 0) return undefined;
if (!isUsableChatModel(model)) return undefined;
const inputs = model.modalities?.input ?? [];
const output = model.limit?.output;
const thinking = modelsDevThinkingOptions(model.reasoning_options);
const input = model.limit?.input;
const maxInputTokens =
typeof input === 'number' && Number.isInteger(input) && input > 0
? Math.min(input, context)
: undefined;
return {
provider: providerId,
model: model.id,
displayName: typeof model.name === 'string' && model.name.length > 0 ? model.name : undefined,
maxContextSize: context,
maxInputSize: maxInputTokens,
maxOutputSize: typeof output === 'number' && output > 0 ? output : undefined,
reasoningKey: modelsDevReasoningKey(model.interleaved),
supportEfforts: thinking.efforts,
offEffort: thinking.offEffort,
alwaysThinking: thinking.alwaysThinking,
capability: {
image_in: inputs.includes('image'),
video_in: inputs.includes('video'),
audio_in: inputs.includes('audio'),
thinking:
Boolean(model.reasoning) || thinking.efforts !== undefined || thinking.hasToggle,
tool_use: model.tool_call ?? true,
dynamically_loaded_tools: model.dynamically_loaded_tools === true,
},
};
}
function modelsDevThinkingOptions(options: ModelsDevModelEntry['reasoning_options']): {
readonly efforts: readonly string[] | undefined;
readonly offEffort: string | undefined;
readonly hasToggle: boolean;
readonly alwaysThinking: boolean | undefined;
} {
if (!Array.isArray(options)) {
return {
efforts: undefined,
offEffort: undefined,
hasToggle: false,
alwaysThinking: undefined,
};
}
let efforts: readonly string[] | undefined;
let offEffort: string | undefined;
let hasToggle = false;
for (const option of options) {
if (option?.type === 'toggle') {
hasToggle = true;
continue;
}
if (option?.type !== 'effort' || !Array.isArray(option.values)) continue;
const hasNullTier = (option.values as unknown[]).some((value) => value === null);
const levels = (option.values as unknown[]).filter(
(value: unknown): value is string => typeof value === 'string' && value.length > 0,
);
const off = levels.find((value) => value.toLowerCase() === 'none');
if (off !== undefined) offEffort = off;
else if (hasNullTier) offEffort = 'none';
const selectable = levels.filter((value) => value.toLowerCase() !== 'none');
if (selectable.length > 0) efforts = selectable;
}
const alwaysThinking =
efforts !== undefined && offEffort === undefined && !hasToggle ? true : undefined;
return { efforts, offEffort, hasToggle, alwaysThinking };
}
function modelsDevReasoningKey(interleaved: ModelsDevModelEntry['interleaved']): string | undefined {
if (typeof interleaved !== 'object' || interleaved === null) return undefined;
const field = interleaved.field?.trim();
return field !== undefined && field.length > 0 ? field : undefined;
}
export function modelsDevProviderModels(
providerId: string,
entry: ModelsDevProviderEntry,
): CatalogModelDefinition[] {
const providerWire = resolveModelsDevWire(entry);
return Object.values(entry.models ?? {})
.map((raw) => {
const resolved = applyModelProviderOverride(
normalizeModelsDevModel(providerId, raw),
raw,
entry,
providerWire,
);
return resolved === undefined
? undefined
: dropAlwaysThinkingForWire(resolved.model, resolved.wire);
})
.filter((model): model is CatalogModelDefinition => model !== undefined);
}
function dropAlwaysThinkingForWire(
model: CatalogModelDefinition,
wire: ModelsDevWire | undefined,
): CatalogModelDefinition {
return model.alwaysThinking === true && (wire === 'anthropic' || wire === 'kimi')
? { ...model, alwaysThinking: undefined }
: model;
}
function applyModelProviderOverride(
model: CatalogModelDefinition | undefined,
raw: ModelsDevModelEntry,
entry: ModelsDevProviderEntry,
providerWire: ModelsDevWire | undefined,
): { model: CatalogModelDefinition; wire: ModelsDevWire | undefined } | undefined {
if (model === undefined) return undefined;
const override = raw.provider;
if (override === undefined) return { model, wire: providerWire };
const overrideNpm = typeof override.npm === 'string' ? override.npm.toLowerCase() : undefined;
if (
overrideNpm !== undefined &&
(overrideNpm.includes('amazon-bedrock') || overrideNpm.includes('cohere'))
) {
return undefined;
}
const overrideWire =
overrideNpm !== undefined ? (inferOverrideWire(overrideNpm) ?? 'openai') : providerWire;
if (overrideWire === undefined) return { model, wire: providerWire };
const rawApi = override.api;
const api = rawApi ?? entry.api;
const usableApi =
typeof api === 'string' && api.length > 0 && !api.includes('${') ? api : undefined;
if (overrideWire === providerWire) {
if (typeof rawApi === 'string' && rawApi.includes('${')) return undefined;
if (usableApi !== undefined && usableApi !== entry.api) {
return {
model: { ...model, baseUrl: adaptBaseUrlForWire(usableApi, overrideWire) },
wire: overrideWire,
};
}
return { model, wire: overrideWire };
}
if (overrideWire === 'anthropic' && usableApi !== undefined) {
return {
model: {
...model,
protocol: 'anthropic',
baseUrl: adaptBaseUrlForWire(usableApi, 'anthropic'),
},
wire: 'anthropic',
};
}
return undefined;
}
function inferOverrideWire(npm: string): ModelsDevWire | undefined {
const normalized = npm.toLowerCase();
if (normalized.includes('anthropic')) return 'anthropic';
if (normalized.includes('vertex')) return 'google-vertex';
if (normalized.includes('google')) return 'google-genai';
if (normalized.includes('openai')) return 'openai';
return undefined;
}