From 463c8692dab455984cbe2c29cfdc02bf3a154d2f Mon Sep 17 00:00:00 2001 From: ItzCrazyKns <95534749+ItzCrazyKns@users.noreply.github.com> Date: Tue, 8 Apr 2025 16:00:45 +0530 Subject: [PATCH] feat(providers): add `models.json` for models list --- data/.gitignore | 1 + data/models.json | 157 ++++++++++++++++++++++++++++++ src/lib/providers/anthropic.ts | 40 ++------ src/lib/providers/gemini.ts | 52 ++-------- src/lib/providers/groq.ts | 80 ++------------- src/lib/providers/index.ts | 95 ++++++++++++------ src/lib/providers/ollama.ts | 82 +++++++++------- src/lib/providers/openai.ts | 48 ++------- src/lib/providers/transformers.ts | 49 +++++----- 9 files changed, 326 insertions(+), 278 deletions(-) create mode 100644 data/models.json diff --git a/data/.gitignore b/data/.gitignore index d6b7ef3..e344062 100644 --- a/data/.gitignore +++ b/data/.gitignore @@ -1,2 +1,3 @@ * +!models.json !.gitignore diff --git a/data/models.json b/data/models.json new file mode 100644 index 0000000..8e95e6b --- /dev/null +++ b/data/models.json @@ -0,0 +1,157 @@ +{ + "_comment": "Ollama models are fetched from the Ollama API, so they are not included here.", + "chatModels": { + "openai": [ + { + "displayName": "GPT-3.5 Turbo", + "key": "gpt-3.5-turbo" + }, + { + "displayName": "GPT-4", + "key": "gpt-4" + }, + { + "displayName": "GPT-4 Turbo", + "key": "gpt-4-turbo" + }, + { + "displayName": "GPT-4 Omni", + "key": "gpt-4o" + }, + { + "displayName": "GPT-4 Omni Mini", + "key": "gpt-4o-mini" + } + ], + "groq": [ + { + "displayName": "Gemma2 9B IT", + "key": "gemma2-9b-it" + }, + { + "displayName": "Llama 3.3 70B Versatile", + "key": "llama-3.3-70b-versatile" + }, + { + "displayName": "Llama 3.1 8B Instant", + "key": "llama-3.1-8b-instant" + }, + { + "displayName": "Llama3 70B 8192", + "key": "llama3-70b-8192" + }, + { + "displayName": "Llama3 8B 8192", + "key": "llama3-8b-8192" + }, + { + "displayName": "Mixtral 8x7B 32768", + "key": "mixtral-8x7b-32768" + }, + { + "displayName": "Qwen QWQ 32B (Preview)", + "key": "qwen-qwq-32b" + }, + { + "displayName": "Mistral Saba 24B (Preview)", + "key": "mistral-saba-24b" + }, + { + "displayName": "DeepSeek R1 Distill Llama 70B (Preview)", + "key": "deepseek-r1-distill-llama-70b" + } + ], + "gemini": [ + { + "displayName": "Gemini 2.5 Pro Experimental", + "key": "gemini-2.5-pro-exp-03-25" + }, + { + "displayName": "Gemini 2.0 Flash", + "key": "gemini-2.0-flash" + }, + { + "displayName": "Gemini 2.0 Flash-Lite", + "key": "gemini-2.0-flash-lite" + }, + { + "displayName": "Gemini 2.0 Flash Thinking Experimental", + "key": "gemini-2.0-flash-thinking-exp-01-21" + }, + { + "displayName": "Gemini 1.5 Flash", + "key": "gemini-1.5-flash" + }, + { + "displayName": "Gemini 1.5 Flash-8B", + "key": "gemini-1.5-flash-8b" + }, + { + "displayName": "Gemini 1.5 Pro", + "key": "gemini-1.5-pro" + } + ], + "anthropic": [ + { + "displayName": "Claude 3.7 Sonnet", + "key": "claude-3-7-sonnet-20250219" + }, + { + "displayName": "Claude 3.5 Haiku", + "key": "claude-3-5-haiku-20241022" + }, + { + "displayName": "Claude 3.5 Sonnet v2", + "key": "claude-3-5-sonnet-20241022" + }, + { + "displayName": "Claude 3.5 Sonnet", + "key": "claude-3-5-sonnet-20240620" + }, + { + "displayName": "Claude 3 Opus", + "key": "claude-3-opus-20240229" + }, + { + "displayName": "Claude 3 Sonnet", + "key": "claude-3-sonnet-20240229" + }, + { + "displayName": "Claude 3 Haiku", + "key": "claude-3-haiku-20240307" + } + ] + }, + "embeddingModels": { + "openai": [ + { + "displayName": "Text Embedding 3 Large", + "key": "text-embedding-3-large" + }, + { + "displayName": "Text Embedding 3 Small", + "key": "text-embedding-3-small" + } + ], + "gemini": [ + { + "displayName": "Gemini Embedding", + "key": "gemini-embedding-exp" + } + ], + "transformers": [ + { + "displayName": "BGE Small", + "key": "xenova-bge-small-en-v1.5" + }, + { + "displayName": "GTE Small", + "key": "xenova-gte-small" + }, + { + "displayName": "Bert Multilingual", + "key": "xenova-bert-base-multilingual-uncased" + } + ] + } +} diff --git a/src/lib/providers/anthropic.ts b/src/lib/providers/anthropic.ts index 7ecde4b..16026d5 100644 --- a/src/lib/providers/anthropic.ts +++ b/src/lib/providers/anthropic.ts @@ -1,48 +1,22 @@ import { ChatAnthropic } from '@langchain/anthropic'; -import { ChatModel } from '.'; +import { ChatModel, getModelsList, RawModel } from '.'; import { getAnthropicApiKey } from '../config'; import { BaseChatModel } from '@langchain/core/language_models/chat_models'; -const anthropicChatModels: Record[] = [ - { - displayName: 'Claude 3.7 Sonnet', - key: 'claude-3-7-sonnet-20250219', - }, - { - displayName: 'Claude 3.5 Haiku', - key: 'claude-3-5-haiku-20241022', - }, - { - displayName: 'Claude 3.5 Sonnet v2', - key: 'claude-3-5-sonnet-20241022', - }, - { - displayName: 'Claude 3.5 Sonnet', - key: 'claude-3-5-sonnet-20240620', - }, - { - displayName: 'Claude 3 Opus', - key: 'claude-3-opus-20240229', - }, - { - displayName: 'Claude 3 Sonnet', - key: 'claude-3-sonnet-20240229', - }, - { - displayName: 'Claude 3 Haiku', - key: 'claude-3-haiku-20240307', - }, -]; +const loadModels = () => { + return getModelsList()?.['chatModels']['anthropic'] as unknown as RawModel[] +} export const loadAnthropicChatModels = async () => { const anthropicApiKey = getAnthropicApiKey(); - if (!anthropicApiKey) return {}; + const models = loadModels() + try { const chatModels: Record = {}; - anthropicChatModels.forEach((model) => { + models.forEach((model) => { chatModels[model.key] = { displayName: model.displayName, model: new ChatAnthropic({ diff --git a/src/lib/providers/gemini.ts b/src/lib/providers/gemini.ts index b0fa887..0320882 100644 --- a/src/lib/providers/gemini.ts +++ b/src/lib/providers/gemini.ts @@ -3,57 +3,24 @@ import { GoogleGenerativeAIEmbeddings, } from '@langchain/google-genai'; import { getGeminiApiKey } from '../config'; -import { ChatModel, EmbeddingModel } from '.'; +import { ChatModel, EmbeddingModel, getModelsList, RawModel } from '.'; import { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { Embeddings } from '@langchain/core/embeddings'; -const geminiChatModels: Record[] = [ - { - displayName: 'Gemini 2.5 Pro Experimental', - key: 'gemini-2.5-pro-exp-03-25', - }, - { - displayName: 'Gemini 2.0 Flash', - key: 'gemini-2.0-flash', - }, - { - displayName: 'Gemini 2.0 Flash-Lite', - key: 'gemini-2.0-flash-lite', - }, - { - displayName: 'Gemini 2.0 Flash Thinking Experimental', - key: 'gemini-2.0-flash-thinking-exp-01-21', - }, - { - displayName: 'Gemini 1.5 Flash', - key: 'gemini-1.5-flash', - }, - { - displayName: 'Gemini 1.5 Flash-8B', - key: 'gemini-1.5-flash-8b', - }, - { - displayName: 'Gemini 1.5 Pro', - key: 'gemini-1.5-pro', - }, -]; - -const geminiEmbeddingModels: Record[] = [ - { - displayName: 'Gemini Embedding', - key: 'gemini-embedding-exp', - }, -]; +const loadModels = (modelType: 'chat' | 'embedding') => { + return getModelsList()?.[modelType === 'chat' ? 'chatModels' : 'embeddingModels']['gemini'] as unknown as RawModel[] +} export const loadGeminiChatModels = async () => { const geminiApiKey = getGeminiApiKey(); - if (!geminiApiKey) return {}; + const models = loadModels('chat'); + try { const chatModels: Record = {}; - geminiChatModels.forEach((model) => { + models.forEach((model) => { chatModels[model.key] = { displayName: model.displayName, model: new ChatGoogleGenerativeAI({ @@ -73,13 +40,14 @@ export const loadGeminiChatModels = async () => { export const loadGeminiEmbeddingModels = async () => { const geminiApiKey = getGeminiApiKey(); - if (!geminiApiKey) return {}; + const models = loadModels('embedding'); + try { const embeddingModels: Record = {}; - geminiEmbeddingModels.forEach((model) => { + models.forEach((model) => { embeddingModels[model.key] = { displayName: model.displayName, model: new GoogleGenerativeAIEmbeddings({ diff --git a/src/lib/providers/groq.ts b/src/lib/providers/groq.ts index beeba02..638107f 100644 --- a/src/lib/providers/groq.ts +++ b/src/lib/providers/groq.ts @@ -1,88 +1,22 @@ import { ChatOpenAI } from '@langchain/openai'; import { getGroqApiKey } from '../config'; -import { ChatModel } from '.'; +import { ChatModel, getModelsList, RawModel } from '.'; import { BaseChatModel } from '@langchain/core/language_models/chat_models'; -const groqChatModels: Record[] = [ - { - displayName: 'Gemma2 9B IT', - key: 'gemma2-9b-it', - }, - { - displayName: 'Llama 3.3 70B Versatile', - key: 'llama-3.3-70b-versatile', - }, - { - displayName: 'Llama 3.1 8B Instant', - key: 'llama-3.1-8b-instant', - }, - { - displayName: 'Llama3 70B 8192', - key: 'llama3-70b-8192', - }, - { - displayName: 'Llama3 8B 8192', - key: 'llama3-8b-8192', - }, - { - displayName: 'Mixtral 8x7B 32768', - key: 'mixtral-8x7b-32768', - }, - { - displayName: 'Qwen QWQ 32B (Preview)', - key: 'qwen-qwq-32b', - }, - { - displayName: 'Mistral Saba 24B (Preview)', - key: 'mistral-saba-24b', - }, - { - displayName: 'Qwen 2.5 Coder 32B (Preview)', - key: 'qwen-2.5-coder-32b', - }, - { - displayName: 'Qwen 2.5 32B (Preview)', - key: 'qwen-2.5-32b', - }, - { - displayName: 'DeepSeek R1 Distill Qwen 32B (Preview)', - key: 'deepseek-r1-distill-qwen-32b', - }, - { - displayName: 'DeepSeek R1 Distill Llama 70B (Preview)', - key: 'deepseek-r1-distill-llama-70b', - }, - { - displayName: 'Llama 3.3 70B SpecDec (Preview)', - key: 'llama-3.3-70b-specdec', - }, - { - displayName: 'Llama 3.2 1B Preview (Preview)', - key: 'llama-3.2-1b-preview', - }, - { - displayName: 'Llama 3.2 3B Preview (Preview)', - key: 'llama-3.2-3b-preview', - }, - { - displayName: 'Llama 3.2 11B Vision Preview (Preview)', - key: 'llama-3.2-11b-vision-preview', - }, - { - displayName: 'Llama 3.2 90B Vision Preview (Preview)', - key: 'llama-3.2-90b-vision-preview', - }, -]; +const loadModels = () => { + return getModelsList()?.chatModels['groq'] as unknown as RawModel[] +} export const loadGroqChatModels = async () => { const groqApiKey = getGroqApiKey(); - if (!groqApiKey) return {}; + const models = loadModels() + try { const chatModels: Record = {}; - groqChatModels.forEach((model) => { + models.forEach((model) => { chatModels[model.key] = { displayName: model.displayName, model: new ChatOpenAI({ diff --git a/src/lib/providers/index.ts b/src/lib/providers/index.ts index c32d0fa..d8a84e8 100644 --- a/src/lib/providers/index.ts +++ b/src/lib/providers/index.ts @@ -1,26 +1,39 @@ -import { Embeddings } from '@langchain/core/embeddings'; -import { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import { loadOpenAIChatModels, loadOpenAIEmbeddingModels } from './openai'; +import { Embeddings } from '@langchain/core/embeddings' +import { BaseChatModel } from '@langchain/core/language_models/chat_models' +import { loadOpenAIChatModels, loadOpenAIEmbeddingModels } from './openai' import { getCustomOpenaiApiKey, getCustomOpenaiApiUrl, getCustomOpenaiModelName, -} from '../config'; -import { ChatOpenAI } from '@langchain/openai'; -import { loadOllamaChatModels, loadOllamaEmbeddingModels } from './ollama'; -import { loadGroqChatModels } from './groq'; -import { loadAnthropicChatModels } from './anthropic'; -import { loadGeminiChatModels, loadGeminiEmbeddingModels } from './gemini'; -import { loadTransformersEmbeddingsModels } from './transformers'; +} from '../config' +import { ChatOpenAI } from '@langchain/openai' +import { loadOllamaChatModels, loadOllamaEmbeddingModels } from './ollama' +import { loadGroqChatModels } from './groq' +import { loadAnthropicChatModels } from './anthropic' +import { loadGeminiChatModels, loadGeminiEmbeddingModels } from './gemini' +import { loadTransformersEmbeddingsModels } from './transformers' +import path from 'path' +import fs from 'fs' export interface ChatModel { - displayName: string; - model: BaseChatModel; + displayName: string + model: BaseChatModel } export interface EmbeddingModel { - displayName: string; - model: Embeddings; + displayName: string + model: Embeddings +} + +export type RawModel = { + displayName: string + key: string +} + +type ModelsList = { + [key in "chatModels" | "embeddingModels"]: { + [key: string]: RawModel[] + } } export const chatModelProviders: Record< @@ -32,7 +45,7 @@ export const chatModelProviders: Record< groq: loadGroqChatModels, anthropic: loadAnthropicChatModels, gemini: loadGeminiChatModels, -}; +} export const embeddingModelProviders: Record< string, @@ -42,21 +55,43 @@ export const embeddingModelProviders: Record< ollama: loadOllamaEmbeddingModels, gemini: loadGeminiEmbeddingModels, transformers: loadTransformersEmbeddingsModels, -}; +} + +export const getModelsList = (): ModelsList | null => { + const modelFile = path.join(process.cwd(), 'data/models.json') + try { + const content = fs.readFileSync(modelFile, 'utf-8') + return JSON.parse(content) as ModelsList + } catch (err) { + console.error(`Error reading models file: ${err}`) + return null + } +} + +export const updateModelsList = (models: ModelsList) => { + try { + const modelFile = path.join(process.cwd(), 'data/models.json') + const content = JSON.stringify(models, null, 2) + + fs.writeFileSync(modelFile, content, 'utf-8') + } catch(err) { + console.error(`Error updating models file: ${err}`) + } +} export const getAvailableChatModelProviders = async () => { - const models: Record> = {}; + const models: Record> = {} for (const provider in chatModelProviders) { - const providerModels = await chatModelProviders[provider](); + const providerModels = await chatModelProviders[provider]() if (Object.keys(providerModels).length > 0) { - models[provider] = providerModels; + models[provider] = providerModels } } - const customOpenAiApiKey = getCustomOpenaiApiKey(); - const customOpenAiApiUrl = getCustomOpenaiApiUrl(); - const customOpenAiModelName = getCustomOpenaiModelName(); + const customOpenAiApiKey = getCustomOpenaiApiKey() + const customOpenAiApiUrl = getCustomOpenaiApiUrl() + const customOpenAiModelName = getCustomOpenaiModelName() models['custom_openai'] = { ...(customOpenAiApiKey && customOpenAiApiUrl && customOpenAiModelName @@ -74,20 +109,20 @@ export const getAvailableChatModelProviders = async () => { }, } : {}), - }; + } - return models; -}; + return models +} export const getAvailableEmbeddingModelProviders = async () => { - const models: Record> = {}; + const models: Record> = {} for (const provider in embeddingModelProviders) { - const providerModels = await embeddingModelProviders[provider](); + const providerModels = await embeddingModelProviders[provider]() if (Object.keys(providerModels).length > 0) { - models[provider] = providerModels; + models[provider] = providerModels } } - return models; -}; + return models +} diff --git a/src/lib/providers/ollama.ts b/src/lib/providers/ollama.ts index 92e98e4..415193e 100644 --- a/src/lib/providers/ollama.ts +++ b/src/lib/providers/ollama.ts @@ -1,24 +1,39 @@ -import axios from 'axios'; -import { getKeepAlive, getOllamaApiEndpoint } from '../config'; -import { ChatModel, EmbeddingModel } from '.'; -import { ChatOllama } from '@langchain/community/chat_models/ollama'; -import { OllamaEmbeddings } from '@langchain/community/embeddings/ollama'; - -export const loadOllamaChatModels = async () => { - const ollamaApiEndpoint = getOllamaApiEndpoint(); - - if (!ollamaApiEndpoint) return {}; +import axios from 'axios' +import { getKeepAlive, getOllamaApiEndpoint } from '../config' +import { ChatModel, EmbeddingModel } from '.' +import { ChatOllama } from '@langchain/community/chat_models/ollama' +import { OllamaEmbeddings } from '@langchain/community/embeddings/ollama' +const loadModels = async (apiURL: string) => { try { - const res = await axios.get(`${ollamaApiEndpoint}/api/tags`, { + const res = await axios.get(`${apiURL}/api/tags`, { headers: { 'Content-Type': 'application/json', }, - }); + }) - const { models } = res.data; + if (res.status !== 200) { + console.error(`Failed to load Ollama models: ${res.data}`) + return [] + } - const chatModels: Record = {}; + const { models } = res.data + + return models + } catch (err) { + console.error(`Error loading Ollama models: ${err}`) + return [] + } +} + +export const loadOllamaChatModels = async () => { + const ollamaApiEndpoint = getOllamaApiEndpoint() + if (!ollamaApiEndpoint) return {} + + const models = await loadModels(ollamaApiEndpoint) + + try { + const chatModels: Record = {} models.forEach((model: any) => { chatModels[model.model] = { @@ -29,31 +44,24 @@ export const loadOllamaChatModels = async () => { temperature: 0.7, keepAlive: getKeepAlive(), }), - }; - }); + } + }) - return chatModels; + return chatModels } catch (err) { - console.error(`Error loading Ollama models: ${err}`); - return {}; + console.error(`Error loading Ollama models: ${err}`) + return {} } -}; +} export const loadOllamaEmbeddingModels = async () => { - const ollamaApiEndpoint = getOllamaApiEndpoint(); + const ollamaApiEndpoint = getOllamaApiEndpoint() + if (!ollamaApiEndpoint) return {} - if (!ollamaApiEndpoint) return {}; + const models = await loadModels(ollamaApiEndpoint) try { - const res = await axios.get(`${ollamaApiEndpoint}/api/tags`, { - headers: { - 'Content-Type': 'application/json', - }, - }); - - const { models } = res.data; - - const embeddingModels: Record = {}; + const embeddingModels: Record = {} models.forEach((model: any) => { embeddingModels[model.model] = { @@ -62,12 +70,12 @@ export const loadOllamaEmbeddingModels = async () => { baseUrl: ollamaApiEndpoint, model: model.model, }), - }; - }); + } + }) - return embeddingModels; + return embeddingModels } catch (err) { - console.error(`Error loading Ollama embeddings models: ${err}`); - return {}; + console.error(`Error loading Ollama embeddings models: ${err}`) + return {} } -}; +} diff --git a/src/lib/providers/openai.ts b/src/lib/providers/openai.ts index 01bacc6..dac082b 100644 --- a/src/lib/providers/openai.ts +++ b/src/lib/providers/openai.ts @@ -1,52 +1,23 @@ import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai'; import { getOpenaiApiKey } from '../config'; -import { ChatModel, EmbeddingModel } from '.'; +import { ChatModel, EmbeddingModel, getModelsList, RawModel } from '.'; import { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { Embeddings } from '@langchain/core/embeddings'; -const openaiChatModels: Record[] = [ - { - displayName: 'GPT-3.5 Turbo', - key: 'gpt-3.5-turbo', - }, - { - displayName: 'GPT-4', - key: 'gpt-4', - }, - { - displayName: 'GPT-4 turbo', - key: 'gpt-4-turbo', - }, - { - displayName: 'GPT-4 omni', - key: 'gpt-4o', - }, - { - displayName: 'GPT-4 omni mini', - key: 'gpt-4o-mini', - }, -]; - -const openaiEmbeddingModels: Record[] = [ - { - displayName: 'Text Embedding 3 Small', - key: 'text-embedding-3-small', - }, - { - displayName: 'Text Embedding 3 Large', - key: 'text-embedding-3-large', - }, -]; +const loadModels = (modelType: 'chat' | 'embedding') => { + return getModelsList()?.[modelType === 'chat' ? 'chatModels' : 'embeddingModels']['openai'] as unknown as RawModel[] +} export const loadOpenAIChatModels = async () => { const openaiApiKey = getOpenaiApiKey(); + const models = loadModels('chat'); - if (!openaiApiKey) return {}; + if (!openaiApiKey || !models) return {}; try { const chatModels: Record = {}; - openaiChatModels.forEach((model) => { + models.forEach((model) => { chatModels[model.key] = { displayName: model.displayName, model: new ChatOpenAI({ @@ -66,13 +37,14 @@ export const loadOpenAIChatModels = async () => { export const loadOpenAIEmbeddingModels = async () => { const openaiApiKey = getOpenaiApiKey(); + const models = loadModels('embedding'); - if (!openaiApiKey) return {}; + if (!openaiApiKey || !models) return {}; try { const embeddingModels: Record = {}; - openaiEmbeddingModels.forEach((model) => { + models.forEach((model) => { embeddingModels[model.key] = { displayName: model.displayName, model: new OpenAIEmbeddings({ diff --git a/src/lib/providers/transformers.ts b/src/lib/providers/transformers.ts index a06dd12..c8d5fd3 100644 --- a/src/lib/providers/transformers.ts +++ b/src/lib/providers/transformers.ts @@ -1,31 +1,30 @@ -import { HuggingFaceTransformersEmbeddings } from '../huggingfaceTransformer'; +import { EmbeddingModel, getModelsList, RawModel } from '.' +import { HuggingFaceTransformersEmbeddings } from '../huggingfaceTransformer' + +const loadModels = () => { + return getModelsList()?.embeddingModels[ + 'transformers' + ] as unknown as RawModel[] +} export const loadTransformersEmbeddingsModels = async () => { try { - const embeddingModels = { - 'xenova-bge-small-en-v1.5': { - displayName: 'BGE Small', - model: new HuggingFaceTransformersEmbeddings({ - modelName: 'Xenova/bge-small-en-v1.5', - }), - }, - 'xenova-gte-small': { - displayName: 'GTE Small', - model: new HuggingFaceTransformersEmbeddings({ - modelName: 'Xenova/gte-small', - }), - }, - 'xenova-bert-base-multilingual-uncased': { - displayName: 'Bert Multilingual', - model: new HuggingFaceTransformersEmbeddings({ - modelName: 'Xenova/bert-base-multilingual-uncased', - }), - }, - }; + const models = loadModels() - return embeddingModels; + const embeddingModels: Record = {} + + models.forEach(model => { + embeddingModels[model.key] = { + displayName: model.displayName, + model: new HuggingFaceTransformersEmbeddings({ + modelName: model.key, + }), + } + }) + + return embeddingModels } catch (err) { - console.error(`Error loading Transformers embeddings model: ${err}`); - return {}; + console.error(`Error loading Transformers embeddings model: ${err}`) + return {} } -}; +}