84 lines
3.0 KiB
TypeScript
84 lines
3.0 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.chat/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-4o-mini', 'gpt-4o', 'gpt-4-turbo', 'gpt-3.5-turbo'],
|
|
anthropic: ['claude-3-5-sonnet-latest', 'claude-3-5-haiku-latest', 'claude-3-opus-latest'],
|
|
google: ['gemini-1.5-flash', 'gemini-1.5-pro', 'gemini-2.0-flash-exp'],
|
|
deepseek: ['deepseek-chat', 'deepseek-coder'],
|
|
minimax: ['abab6.5-chat', 'abab6.5s-chat'],
|
|
mistral: ['mistral-small-latest', 'mistral-medium-latest', 'mistral-large-latest'],
|
|
glm: ['glm-4', 'glm-4-flash'],
|
|
openrouter: ['openai/gpt-4o-mini', 'anthropic/claude-3.5-sonnet', 'deepseek/deepseek-chat'],
|
|
custom: [],
|
|
};
|
|
|
|
/**
|
|
* 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<string[]> {
|
|
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 === 'google') {
|
|
const standardProvider = provider === 'anthropic_custom' ? 'anthropic' : provider;
|
|
return PROVIDER_MODEL_SUGGESTIONS[standardProvider] ?? [];
|
|
}
|
|
|
|
const baseUrl = provider === 'custom'
|
|
? customBaseUrl?.replace(/\/$/, '')
|
|
: PROVIDER_URLS[provider];
|
|
|
|
if (!baseUrl) {
|
|
return PROVIDER_MODEL_SUGGESTIONS[provider] ?? [];
|
|
}
|
|
|
|
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) {
|
|
return PROVIDER_MODEL_SUGGESTIONS[provider] ?? [];
|
|
}
|
|
|
|
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 fetched;
|
|
}
|
|
} catch (err) {
|
|
console.warn(`[fetchLiveModelsForProvider] Failed to fetch live models for ${provider}, using fallbacks:`, err);
|
|
}
|
|
|
|
return PROVIDER_MODEL_SUGGESTIONS[provider] ?? [];
|
|
}
|