Files
Momento/memento-note/lib/ai/models-list.ts
Antigravity afbb0dfc2d
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 6m57s
CI / Deploy production (on server) (push) Successful in 24s
fix(ui): thème, textes et lisibilité restants de la revue
Les boutons suivent la couleur d’apparence, les libellés trop petits ou trop techniques sont clarifiés, et le catalogue des fournisseurs se met à jour tout seul.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-30 21:13:07 +00:00

118 lines
4.1 KiB
TypeScript

import { type AiGatewayProvider } from '@/lib/ai/router';
// Base URLs mapping for providers
const PROVIDER_URLS: Record<string, string> = {
openai: 'https://api.openai.com/v1',
deepseek: 'https://api.deepseek.com/v1',
openrouter: 'https://openrouter.ai/api/v1',
mistral: 'https://api.mistral.ai/v1',
zai: 'https://api.zukijourney.com/v1',
minimax: 'https://api.minimax.io/v1',
glm: 'https://open.bigmodel.ai/api/paas/v4',
};
// Fallback popular models when live fetching fails or for providers without /models endpoint (e.g. Anthropic, Google)
export const PROVIDER_MODEL_SUGGESTIONS: Record<string, string[]> = {
openai: ['gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.6-luna'],
anthropic: ['claude-opus-5', 'claude-sonnet-5', 'claude-fable-5'],
google: ['gemini-3.7-flash', 'gemini-3.6-flash', 'gemini-3.5-flash-lite'],
deepseek: ['deepseek-v4-pro', 'deepseek-v4-flash', 'deepseek-chat'],
minimax: ['MiniMax-M3', 'MiniMax-M2.7', 'MiniMax-M2.5'],
mistral: ['mistral-medium-latest', 'mistral-small-latest', 'mistral-large-latest'],
glm: ['glm-5.3', 'glm-5.3-flash', 'glm-5.2'],
openrouter: ['openai/gpt-5.6-sol', 'anthropic/claude-opus-5', 'google/gemini-3.7-flash'],
zai: ['gpt-5.6-sol', 'claude-sonnet-5', 'gemini-3.7-flash'],
custom: [],
};
/**
* Result of fetching models - includes whether they came from the real API or fallbacks
*/
export interface FetchModelsResult {
models: string[]
fromApi: boolean // true = fetched from provider API, false = fallback suggestions
}
/**
* Dynamically queries the provider's /models endpoint using the user's API Key
* to fetch their actual available models list instead of relying on hardcoded choices.
*/
export async function fetchLiveModelsForProvider(
provider: AiGatewayProvider,
apiKey: string,
customBaseUrl?: string
): Promise<FetchModelsResult> {
try {
// Anthropic and Google do not expose a public list via a simple key GET /models (or need specific formats)
// We fall back to the popular defaults for those.
if (
provider === 'anthropic' ||
provider === 'anthropic_custom' ||
provider === 'custom_anthropic' ||
provider === 'google' ||
provider === 'minimax'
) {
const standardProvider =
provider === 'anthropic_custom' || provider === 'custom_anthropic'
? 'anthropic'
: provider;
const models = PROVIDER_MODEL_SUGGESTIONS[standardProvider] ?? [];
return { models, fromApi: false };
}
const baseUrl = (provider === 'custom' || provider === 'custom_openai')
? customBaseUrl?.replace(/\/$/, '')
: PROVIDER_URLS[provider];
if (!baseUrl) {
const models = PROVIDER_MODEL_SUGGESTIONS[provider] ?? [];
return { models, fromApi: false };
}
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
headers['Authorization'] = `Bearer ${apiKey}`;
if (provider === 'openrouter') {
headers['HTTP-Referer'] = 'https://localhost:3000';
headers['X-Title'] = 'Memento AI';
}
const response = await fetch(`${baseUrl}/models`, {
headers,
signal: AbortSignal.timeout(6000),
});
if (!response.ok) {
console.warn(`[fetchLiveModelsForProvider] API returned ${response.status} for ${provider}`);
const models = PROVIDER_MODEL_SUGGESTIONS[provider] ?? [];
return { models, fromApi: false };
}
const data = await response.json();
const fetched: string[] = (data.data ?? [])
.map((m: any) => m.id || m.name)
.filter(Boolean)
.sort();
if (fetched.length > 0) {
return { models: fetched, fromApi: true };
} else {
console.warn(`[fetchLiveModelsForProvider] API returned empty data array for ${provider}`);
}
} catch (err) {
console.warn(`[fetchLiveModelsForProvider] Failed to fetch live models for ${provider}:`, err);
}
const fallbackProvider =
provider === 'custom_openai'
? 'openai'
: provider === 'custom_anthropic'
? 'anthropic'
: provider === 'anthropic_custom'
? 'anthropic'
: provider;
const models = PROVIDER_MODEL_SUGGESTIONS[fallbackProvider] ?? [];
return { models, fromApi: false };
}