From e7773eafc2e55fa3ed32784bafc8022a82b8ab4b Mon Sep 17 00:00:00 2001 From: eneaxharau Date: Wed, 3 Dec 2025 14:37:38 +0100 Subject: [PATCH 1/4] feat: add dynamic fetching for ollama, lm-studio, anthropic, groq --- cli/commands/config.command.ts | 210 ++++++++------------------------- src/utils/get-models.ts | 117 ++++++++++++++++++ 2 files changed, 165 insertions(+), 162 deletions(-) create mode 100644 src/utils/get-models.ts diff --git a/cli/commands/config.command.ts b/cli/commands/config.command.ts index e294bfc..af68b77 100644 --- a/cli/commands/config.command.ts +++ b/cli/commands/config.command.ts @@ -2,6 +2,7 @@ import * as fs from 'fs'; import * as path from 'path'; import inquirer from 'inquirer'; import chalk from 'chalk'; +import { getModels } from '../../src/utils/get-models'; const CONFIG_FILE = '.codewave.config.json'; @@ -161,176 +162,20 @@ async function initializeConfig(): Promise { value: 'lm-studio', short: 'LM Studio', }, + { name: 'Groq', value: 'groq', short: 'Groq' }, ], default: defaultProvider, }, ]); - // Provider-specific configuration with available models - const providerInfo = { - anthropic: { - defaultModel: 'claude-haiku-4-5-20251001', - models: [ - { - name: 'claude-haiku-4-5-20251001 (recommended) - Cost-optimized for multi-agent discussion (ultra-fast)', - value: 'claude-haiku-4-5-20251001', - }, - { - name: 'claude-sonnet-4-5-20250929 - Latest generation (best quality)', - value: 'claude-sonnet-4-5-20250929', - }, - { - name: 'claude-opus-4-1-20250805 - Most powerful (maximum accuracy)', - value: 'claude-opus-4-1-20250805', - }, - ], - keyFormat: 'sk-ant-...', - url: 'https://console.anthropic.com/', - }, - openai: { - defaultModel: 'gpt-4o-mini', - models: [ - { name: 'gpt-4o-mini (recommended) - Fast and cost-effective', value: 'gpt-4o-mini' }, - { name: 'gpt-4o - Latest multimodal model', value: 'gpt-4o' }, - { name: 'o3-mini - Advanced reasoning (cost-efficient)', value: 'o3-mini-2025-01-31' }, - { name: 'o3 - Most powerful reasoning model', value: 'o3' }, - ], - keyFormat: 'sk-...', - url: 'https://platform.openai.com/', - }, - google: { - defaultModel: 'gemini-2.5-flash', - models: [ - { - name: 'gemini-2.5-flash (recommended) - Best cost-performance ratio', - value: 'gemini-2.5-flash', - }, - { - name: 'gemini-2.5-flash-lite - Fastest and most efficient', - value: 'gemini-2.5-flash-lite', - }, - { name: 'gemini-2.5-pro - Best reasoning capabilities', value: 'gemini-2.5-pro' }, - ], - keyFormat: 'AIza...', - url: 'https://ai.google.dev/', - }, - xai: { - defaultModel: 'grok-4-fast-non-reasoning', - models: [ - { - name: 'grok-4-fast-non-reasoning (recommended) - Latest with 40% fewer tokens', - value: 'grok-4-fast-non-reasoning', - }, - { name: 'grok-4.2 - Polished and refined', value: 'grok-4.2' }, - { name: 'grok-4 - Advanced reasoning model', value: 'grok-4-0709' }, - ], - keyFormat: 'xai-...', - url: 'https://console.x.ai/', - }, - ollama: { - defaultModel: 'gpt-oss-20b', - models: [ - { - name: 'gpt-oss-20b (recommended) - Balanced reasoning and performance', - value: 'gpt-oss-20b', - }, - ], - keyFormat: '(no API key required)', - url: 'https://ollama.com/library', - }, - 'lm-studio': { - defaultModel: 'local-model', // LM Studio often ignores the model name if only one is loaded - models: [ - { - name: 'Load from LM Studio (uses currently loaded model)', - value: 'local-model', - }, - ], - keyFormat: '(no API key required)', - url: 'http://localhost:1234', - }, - groq: { - defaultModel: 'openai/gpt-oss-120b', - models: [ - { - name: 'openai/gpt-oss-120b (recommended) - Balanced reasoning and performance', - value: 'openai/gpt-oss-120b', - }, - ], - keyFormat: 'gsk_...', - url: 'https://console.groq.com/', - }, - }; - - const info = providerInfo[provider as keyof typeof providerInfo]; - - // Select model for the chosen provider - console.log(chalk.cyan(`\nšŸŽÆ Available ${provider} models:\n`)); - console.log( - chalk.gray('šŸ’” CodeWave uses multi-agent discussion (3 rounds) to refine evaluations.') - ); - console.log( - chalk.gray(' Cheaper models like Haiku achieve 95%+ quality through discussion refinement.\n') - ); - - // Use existing model as default if it's valid for this provider, otherwise use provider default - let defaultModel = info.defaultModel; - if (config.llm.model && info.models.some((m) => m.value === config.llm.model)) { - defaultModel = config.llm.model; - } - - const { selectedModel } = await inquirer.prompt([ - { - type: 'list', - name: 'selectedModel', - message: `Choose ${provider} model:`, - choices: info.models, - default: defaultModel, - }, - ]); - - // Show cost comparison for selected provider - const costByProvider = { - anthropic: { - 'claude-haiku-4-5-20251001': '$0.025/commit', - 'claude-sonnet-4-5-20250929': '$0.15/commit', - 'claude-opus-4-1-20250805': '$0.40/commit', - }, - openai: { - 'gpt-4o-mini': '$0.008/commit', - 'gpt-4o': '$0.10/commit', - 'o3-mini-2025-01-31': '$0.20/commit', - o3: '$0.40/commit', - }, - google: { - 'gemini-2.5-flash': '$0.010/commit', - 'gemini-2.5-flash-lite': '$0.006/commit', - 'gemini-2.5-pro': '$0.06/commit', - }, - xai: { - 'grok-4-fast-non-reasoning': '$0.08/commit', - 'grok-4.2': '$0.08/commit', - 'grok-4-0709': '$0.08/commit', - }, - }; - - const providerCosts = costByProvider[provider as keyof typeof costByProvider]; - if (providerCosts) { - const cost = providerCosts[selectedModel as keyof typeof providerCosts]; - if (cost) { - console.log(chalk.gray(`\nāœ“ Selected: ${selectedModel}`)); - console.log(chalk.gray(` Cost: ${cost} (estimated for 3-round multi-agent discussion)`)); - } - } + config.llm.provider = provider; - let apiKey = ''; + let apiKey = null; if (provider !== 'ollama' && provider !== 'lm-studio') { - console.log(chalk.gray(`\nGet your API key at: ${info.url}\n`)); - const existingApiKey = config.apiKeys[provider]; const apiKeyPromptMessage = existingApiKey - ? `Enter ${provider} API key (${info.keyFormat}) [press Enter to keep existing]:` - : `Enter ${provider} API key (${info.keyFormat}):`; + ? `Enter ${provider} API key [press Enter to keep existing]:` + : `Enter ${provider} API key:`; const response = await inquirer.prompt([ { @@ -355,7 +200,48 @@ async function initializeConfig(): Promise { if (apiKey && apiKey.trim().length > 0) { config.apiKeys[provider] = apiKey.trim(); } - config.llm.provider = provider; + + if (provider === 'ollama' || provider === 'lm-studio') { + let existingBaseUrl = config.llm.baseUrl; + const baseUrlPromptMessage = existingBaseUrl + ? `Enter ${provider} base URL [press Enter to keep existing] (${existingBaseUrl}):` + : `Enter ${provider} base URL:`; + + if (!existingBaseUrl) { + existingBaseUrl = + provider === 'ollama' ? 'http://localhost:11434' : 'http://localhost:1234/v1'; + } + const { baseUrl } = await inquirer.prompt([ + { + type: 'input', + name: 'baseUrl', + message: baseUrlPromptMessage, + default: existingBaseUrl, + }, + ]); + config.llm.baseUrl = baseUrl; + } + + const models = await getModels(config); + + // Select model for the chosen provider + console.log(chalk.cyan(`\nšŸŽÆ Available ${provider} models:\n`)); + console.log( + chalk.gray('šŸ’” CodeWave uses multi-agent discussion (3 rounds) to refine evaluations.') + ); + console.log( + chalk.gray(' Cheaper models like Haiku achieve 95%+ quality through discussion refinement.\n') + ); + + const { selectedModel } = await inquirer.prompt([ + { + type: 'list', + name: 'selectedModel', + message: `Choose ${provider} model:`, + choices: models, + default: models[0].value, + }, + ]); config.llm.model = selectedModel; console.log(chalk.green(`\nāœ… Configured to use: ${provider} (${selectedModel})`)); diff --git a/src/utils/get-models.ts b/src/utils/get-models.ts new file mode 100644 index 0000000..bc00eba --- /dev/null +++ b/src/utils/get-models.ts @@ -0,0 +1,117 @@ +import { AppConfig } from 'config/config.interface'; + +interface GenericModel { + name: string; + value: string; +} + +export async function getModels(config: AppConfig): Promise { + const provider = config.llm.provider; + const baseUrl = config.llm.baseUrl; + + if (!provider) { + throw new Error('Provider is required'); + } + + let models: GenericModel[] = []; + + switch (provider) { + case 'ollama': + models = await getOllamaModels(baseUrl); + break; + case 'lm-studio': + models = await getLMStudioModels(baseUrl); + break; + case 'groq': + models = await getGroqModels(config.apiKeys.groq); + break; + case 'anthropic': + models = await getAnthropicModels(config.apiKeys.anthropic); + break; + } + + return models; +} + +interface OllamaModel { + name: string; + model: string; +} + +async function getOllamaModels(baseUrl: string | undefined): Promise { + const response = await fetch(`${baseUrl}/api/tags`); + + if (!response.ok) { + throw new Error(`Failed to fetch models from ${baseUrl}`); + } + + const data = (await response.json()) as { models: OllamaModel[] }; + return data.models.map((model) => ({ + name: model.name, + value: model.model, + })); +} + +interface LMStudioModel { + id: string; +} + +async function getLMStudioModels(baseUrl: string | undefined): Promise { + const response = await fetch(`${baseUrl}/models`); + + if (!response.ok) { + throw new Error(`Failed to fetch models from ${baseUrl}`); + } + const data = (await response.json()) as { data: LMStudioModel[] }; + console.log('data', { data }); + return data.data.map((model) => ({ + name: model.id, + value: model.id, + })); +} + +interface GroqModel { + id: string; +} + +async function getGroqModels(apiKey: string): Promise { + const response = await fetch('https://api.groq.com/openai/v1/models', { + headers: { + Authorization: `Bearer ${apiKey}`, + }, + }); + if (!response.ok) { + throw new Error(`Failed to fetch models from Groq`); + } + const data = (await response.json()) as { data: GroqModel[] }; + return data.data.map((model) => ({ + name: model.id, + value: model.id, + })); +} + +interface AnthropicModel { + id: string; + display_name: string; +} + +async function getAnthropicModels(apiKey: string): Promise { + const response = await fetch('https://api.anthropic.com/v1/models', { + headers: { + 'X-Api-Key': apiKey, + 'anthropic-version': '2023-06-01', + }, + }); + if (!response.ok) { + throw new Error(`Failed to fetch models from Anthropic`); + } + + const data = (await response.json()) as { data: AnthropicModel[] }; + + return data.data.map((model) => ({ + name: model.display_name, + value: model.id, + })); +} + +async function getOpenAIModels(apiKey: string): Promise {} From d61888b453e23db57dded4499f20e9023b6d0938 Mon Sep 17 00:00:00 2001 From: eneaxharau Date: Wed, 3 Dec 2025 15:16:09 +0100 Subject: [PATCH 2/4] feat: add fetching models dynamically for xai, google, openai --- src/utils/get-models.ts | 75 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/src/utils/get-models.ts b/src/utils/get-models.ts index bc00eba..cfd3a0a 100644 --- a/src/utils/get-models.ts +++ b/src/utils/get-models.ts @@ -3,6 +3,10 @@ import { AppConfig } from 'config/config.interface'; interface GenericModel { name: string; value: string; + inputTokenLimit?: number; + outputTokenLimit?: number; + pricePerInputToken?: number; + pricePerOutputToken?: number; } export async function getModels(config: AppConfig): Promise { @@ -28,6 +32,15 @@ export async function getModels(config: AppConfig): Promise { case 'anthropic': models = await getAnthropicModels(config.apiKeys.anthropic); break; + case 'openai': + models = await getOpenAIModels(config.apiKeys.openai); + break; + case 'google': + models = await getGoogleModels(config.apiKeys.google); + break; + case 'xai': + models = await getXAIModels(config.apiKeys.xai); + break; } return models; @@ -114,4 +127,64 @@ async function getAnthropicModels(apiKey: string): Promise { })); } -async function getOpenAIModels(apiKey: string): Promise {} +interface OpenAIModel { + id: string; +} + +async function getOpenAIModels(apiKey: string): Promise { + const response = await fetch('https://api.openai.com/v1/models', { + headers: { + Authorization: `Bearer ${apiKey}`, + }, + }); + if (!response.ok) { + throw new Error(`Failed to fetch models from OpenAI`); + } + const data = (await response.json()) as { data: OpenAIModel[] }; + return data.data.map((model) => ({ + name: model.id, + value: model.id, + })); +} + +interface GoogleModel { + name: string; + displayName: string; + baseModelId: string; + inputTokenLimit: number; + outputTokenLimit: number; +} +async function getGoogleModels(apiKey: string): Promise { + const response = await fetch('https://generativelanguage.googleapis.com/v1beta/models', { + headers: { + 'x-goog-api-key': apiKey, + }, + }); + if (!response.ok) { + throw new Error(`Failed to fetch models from Google`); + } + const data = (await response.json()) as { models: GoogleModel[] }; + return data.models.map((model) => ({ + name: model.displayName, + value: model.name, + })); +} + +interface XAIModel { + id: string; +} +async function getXAIModels(apiKey: string): Promise { + const response = await fetch('https://api.x.ai/v1/models', { + headers: { + Authorization: `Bearer ${apiKey}`, + }, + }); + if (!response.ok) { + throw new Error(`Failed to fetch models from XAI`); + } + const data = (await response.json()) as { data: XAIModel[] }; + return data.data.map((model) => ({ + name: model.id, + value: model.id, + })); +} From 921f3bdd9e478fd256f031186f34e1beab5e644e Mon Sep 17 00:00:00 2001 From: eneaxharau Date: Wed, 3 Dec 2025 15:24:19 +0100 Subject: [PATCH 3/4] feat: add new langchain module for xai and fix prettier issues --- README.md | 1 + cli/utils/shared.utils.ts | 5 ++--- package-lock.json | 16 ++++++++++++++++ package.json | 1 + src/llm/llm-service.ts | 10 ++++------ src/orchestrator/commit-evaluation-graph.ts | 5 ++--- 6 files changed, 26 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index e7eb4b4..18c5729 100644 --- a/README.md +++ b/README.md @@ -972,6 +972,7 @@ Choose your LLM provider and model based on your needs and budget: - **Alternatives**: openai/gpt-oss-20b **Ollama (Local LLMs — Free)** + - Run models entirely on your machine (no API key required) - Supports **Llama 3**, **Mistral**, **Gemma 2**, and more - Works offline once the model is pulled diff --git a/cli/utils/shared.utils.ts b/cli/utils/shared.utils.ts index 3db2ca5..4a0a035 100644 --- a/cli/utils/shared.utils.ts +++ b/cli/utils/shared.utils.ts @@ -433,9 +433,8 @@ export async function createEvaluationDirectory( * Calculate averaged metrics from agent results using weighted averaging (matching report calculations) */ async function calculateAveragedMetrics(evaluationDir: string): Promise { - const { MetricsCalculationService } = await import( - '../../src/services/metrics-calculation.service.js' - ); + const { MetricsCalculationService } = + await import('../../src/services/metrics-calculation.service.js'); return MetricsCalculationService.loadMetricsFromDirectory(evaluationDir); } diff --git a/package-lock.json b/package-lock.json index 6ab1383..d6cd65c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@langchain/langgraph": "^1.0.2", "@langchain/ollama": "^1.0.3", "@langchain/openai": "^1.1.3", + "@langchain/xai": "^1.0.2", "@types/cli-progress": "^3.11.6", "@types/inquirer": "^9.0.9", "chalk": "^4.1.2", @@ -525,6 +526,21 @@ "@langchain/core": "^1.0.0" } }, + "node_modules/@langchain/xai": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@langchain/xai/-/xai-1.0.2.tgz", + "integrity": "sha512-ImwVq5wWzBZXvkUNFE02qw11rMK7a+1f537xVisgoaRODng5oQv4AfPlNEBMT2MKLv4C6POSY1rhC7f/YKriGQ==", + "license": "MIT", + "dependencies": { + "@langchain/openai": "1.1.3" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@langchain/core": "^1.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", diff --git a/package.json b/package.json index fb54551..4b6d739 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "@langchain/langgraph": "^1.0.2", "@langchain/ollama": "^1.0.3", "@langchain/openai": "^1.1.3", + "@langchain/xai": "^1.0.2", "@types/cli-progress": "^3.11.6", "@types/inquirer": "^9.0.9", "chalk": "^4.1.2", diff --git a/src/llm/llm-service.ts b/src/llm/llm-service.ts index a702f1a..f75d429 100644 --- a/src/llm/llm-service.ts +++ b/src/llm/llm-service.ts @@ -3,6 +3,7 @@ import { ChatAnthropic } from '@langchain/anthropic'; import { ChatGoogleGenerativeAI } from '@langchain/google-genai'; import { ChatOllama } from '@langchain/ollama'; import { ChatGroq } from '@langchain/groq'; +import { ChatXAI } from '@langchain/xai'; import { AppConfig } from '../config/config.interface'; export class LLMService { @@ -76,14 +77,11 @@ export class LLMService { }); case 'xai': - return new ChatOpenAI({ - openAIApiKey: apiKey, + return new ChatXAI({ + apiKey, temperature, maxTokens, - modelName: model, - configuration: { - baseURL: 'https://api.x.ai/v1', - }, + model, }); case 'groq': { return new ChatGroq({ diff --git a/src/orchestrator/commit-evaluation-graph.ts b/src/orchestrator/commit-evaluation-graph.ts index 9b96c55..b5ec78e 100644 --- a/src/orchestrator/commit-evaluation-graph.ts +++ b/src/orchestrator/commit-evaluation-graph.ts @@ -276,9 +276,8 @@ export function createCommitEvaluationGraph(agentRegistry: AgentRegistry, config console.log('šŸ“ Generating developer overview from commit diff...'); - const { DeveloperOverviewGenerator } = await import( - '../services/developer-overview-generator.js' - ); + const { DeveloperOverviewGenerator } = + await import('../services/developer-overview-generator.js'); const { LLMService } = await import('../llm/llm-service.js'); const generator = new DeveloperOverviewGenerator(config); From fd2ae67f626f1ec1daa450dd8bc95ed8d4ccc2dc Mon Sep 17 00:00:00 2001 From: eneaxharau Date: Fri, 5 Dec 2025 18:31:08 +0100 Subject: [PATCH 4/4] feat: create a file for model information --- cli/commands/config.command.ts | 16 + cli/commands/evaluate-command.ts | 2 +- src/config/config.interface.ts | 4 +- src/config/default-config.ts | 2 - src/constants/provider-models.ts | 500 +++++++++++++++++++++++++++++++ src/types/model.types.ts | 48 +++ src/utils/get-models.ts | 151 +--------- 7 files changed, 580 insertions(+), 143 deletions(-) create mode 100644 src/constants/provider-models.ts create mode 100644 src/types/model.types.ts diff --git a/cli/commands/config.command.ts b/cli/commands/config.command.ts index af68b77..da643f7 100644 --- a/cli/commands/config.command.ts +++ b/cli/commands/config.command.ts @@ -240,9 +240,25 @@ async function initializeConfig(): Promise { message: `Choose ${provider} model:`, choices: models, default: models[0].value, + loop: false, }, ]); config.llm.model = selectedModel; + const metadata = models.find((model) => model.value === selectedModel); + + if (metadata) { + const inputPricePerToken = parseFloat(metadata.pricing.input); + const outputPricePerToken = parseFloat(metadata.pricing.output); + const cost = (inputPricePerToken + outputPricePerToken) * DEFAULT_CONFIG.llm.maxTokens * 3; + if (cost) { + console.log(chalk.gray(`\nāœ“ Selected: ${chalk.cyanBright(selectedModel)}`)); + console.log( + chalk.gray( + ` Cost: ${chalk.green(`$${cost.toFixed(2)} USD`)} per 3-round multi-agent discussion (estimated)` + ) + ); + } + } console.log(chalk.green(`\nāœ… Configured to use: ${provider} (${selectedModel})`)); diff --git a/cli/commands/evaluate-command.ts b/cli/commands/evaluate-command.ts index a46b39d..4ef6dd5 100644 --- a/cli/commands/evaluate-command.ts +++ b/cli/commands/evaluate-command.ts @@ -149,7 +149,7 @@ export async function runEvaluateCommand(args: string[]) { // Get API key for selected provider const provider = config.llm.provider; - const apiKey = config.apiKeys[provider]; + const apiKey = config.apiKeys?.[provider as keyof typeof config.apiKeys]; if (!apiKey) { console.log(chalk.red(`\nāŒ No API key configured for provider: ${provider}\n`)); diff --git a/src/config/config.interface.ts b/src/config/config.interface.ts index 23ce8b6..bc8f18c 100644 --- a/src/config/config.interface.ts +++ b/src/config/config.interface.ts @@ -9,8 +9,6 @@ export interface AppConfig { openai: string; google: string; xai: string; - ollama: string; - 'lm-studio': string; groq: string; }; llm: { @@ -18,7 +16,7 @@ export interface AppConfig { model: string; temperature: number; maxTokens: number; - baseUrl?: string; + baseUrl?: string | undefined; }; agents: { enabled: string[]; diff --git a/src/config/default-config.ts b/src/config/default-config.ts index f6398e1..a6c41e2 100644 --- a/src/config/default-config.ts +++ b/src/config/default-config.ts @@ -9,8 +9,6 @@ export const DEFAULT_CONFIG: AppConfig = { openai: '', google: '', xai: '', - ollama: '', - 'lm-studio': '', groq: '', }, llm: { diff --git a/src/constants/provider-models.ts b/src/constants/provider-models.ts new file mode 100644 index 0000000..2831939 --- /dev/null +++ b/src/constants/provider-models.ts @@ -0,0 +1,500 @@ +export const PROVIDER_MODELS = { + groq: [ + { + name: 'Qwen 3 32B - [High Intelligence, Low Cost, Super Fast Speed]', + value: 'qwen/qwen3-32b', + pricing: { + input: '0.00000029', + output: '0.00000059', + }, + }, + { + name: 'meta-llama/llama-4-scout-17b-16e-instruct - [Medium Intelligence, Low Cost, Super Fast Speed]', + value: 'meta-llama/llama-4-scout-17b-16e-instruct', + pricing: { + input: '0.00000011', + output: '0.00000034', + }, + }, + { + name: 'llama-3.3-70b-versatile - [High Intelligence, Low Cost, Super Fast Speed]', + value: 'llama-3.3-70b-versatile', + pricing: { + input: '0.00000059', + output: '0.00000079', + }, + }, + { + name: 'moonshotai/kimi-k2-instruct - [Medium Intelligence, Low Cost, Super Fast Speed]', + value: 'moonshotai/kimi-k2-instruct', + pricing: { + input: '0.000001', + output: '0.000003', + }, + }, + { + name: 'moonshotai/kimi-k2-instruct-0905 - [Medium Intelligence, Low Cost, Super Fast Speed]', + value: 'moonshotai/kimi-k2-instruct-0905', + pricing: { + input: '0.000001', + output: '0.000003', + }, + }, + { + name: 'meta-llama/llama-4-maverick-17b-128e-instruct - [Medium Intelligence, Low Cost, Super Fast Speed]', + value: 'meta-llama/llama-4-maverick-17b-128e-instruct', + pricing: { + input: '0.0000002', + output: '0.0000006', + }, + }, + { + name: 'GPT-OSS 120B - [Medium-High Intelligence, Low Cost, Super Fast Speed]', + value: 'openai/gpt-oss-120b', + pricing: { + input: '0.00000015', + output: '0.0000006', + }, + }, + { + name: 'GPT-OSS 20B - [Medium Intelligence, Low Cost, Super Fast Speed]', + value: 'openai/gpt-oss-20b', + pricing: { + input: '0.000000075', + output: '0.0000003', + }, + }, + { + name: 'llama-3.1-8b-instant - [Medium Intelligence, Low Cost, Super Fast Speed]', + value: 'llama-3.1-8b-instant', + pricing: { + input: '0.00000005', + output: '0.00000008', + }, + }, + ], + anthropic: [ + { + name: 'Claude Opus 4.5 - [High Intelligence, High Cost, Slow Speed]', + value: 'claude-opus-4-5-20251101', + pricing: { + input: '0.000005', + output: '0.000025', + }, + }, + { + name: 'Claude Haiku 4.5 - [Medium Intelligence, Low Cost, Fast Speed]', + value: 'claude-haiku-4-5-20251001', + pricing: { + input: '0.000001', + output: '0.000005', + }, + }, + { + name: 'Claude Sonnet 4.5 - [High Intelligence, Medium Cost, Standard Speed]', + value: 'claude-sonnet-4-5-20250929', + pricing: { + input: '0.000003', + output: '0.000015', + }, + }, + { + name: 'Claude Opus 4.1 - [High Intelligence, High Cost, Slow Speed]', + value: 'claude-opus-4-1-20250805', + pricing: { + input: '0.000015', + output: '0.000075', + }, + }, + { + name: 'Claude Opus 4 - [High Intelligence, High Cost, Slow Speed]', + value: 'claude-opus-4-20250514', + pricing: { + input: '0.000015', + output: '0.000075', + }, + }, + { + name: 'Claude Sonnet 4 - [High Intelligence, Medium Cost, Standard Speed]', + value: 'claude-sonnet-4-20250514', + pricing: { + input: '0.000003', + output: '0.000015', + }, + }, + { + name: 'Claude Sonnet 3.7 - [Medium Intelligence, Medium Cost, Standard Speed]', + value: 'claude-3-7-sonnet-20250219', + pricing: { + input: '0.000003', + output: '0.000015', + }, + }, + { + name: 'Claude Haiku 3.5 - [Medium Intelligence, Low Cost, Fast Speed]', + value: 'claude-3-5-haiku-20241022', + pricing: { + input: '0.0000008', + output: '0.000004', + }, + }, + { + name: 'Claude Haiku 3 - [Medium Intelligence, Low Cost, Fast Speed]', + value: 'claude-3-haiku-20240307', + pricing: { + input: '0.00000025', + output: '0.00000125', + }, + }, + { + name: 'Claude Opus 3 - [Medium Intelligence, High Cost, Slow Speed]', + value: 'claude-3-opus-20240229', + pricing: { + input: '0.000015', + output: '0.000075', + }, + }, + ], + openai: [ + { + name: 'gpt-4 - [Medium Intelligence, High Cost, Standard Speed]', + value: 'gpt-4', + pricing: { + input: '0.00003', + output: '0.00006', + }, + }, + { + name: 'gpt-3.5-turbo - [Medium Intelligence, Low Cost, Fast Speed]', + value: 'gpt-3.5-turbo', + pricing: { + input: '0.0000005', + output: '0.0000015', + }, + }, + { + name: 'gpt-5.1-codex-mini - [Medium-High Intelligence, Medium Cost, Fast Speed]', + value: 'gpt-5.1-codex-mini', + pricing: { + input: '0.00000025', + output: '0.000002', + }, + }, + { + name: 'gpt-5.1 - [High Intelligence, High Cost, Standard Speed]', + value: 'gpt-5.1', + pricing: { + input: '0.00000125', + output: '0.00001', + }, + }, + { + name: 'gpt-5.1-codex - [High Intelligence, High Cost, Standard Speed]', + value: 'gpt-5.1-codex', + pricing: { + input: '0.00000125', + output: '0.00001', + }, + }, + { + name: 'gpt-3.5-turbo-instruct - [Medium Intelligence, Low Cost, Fast Speed]', + value: 'gpt-3.5-turbo-instruct', + pricing: { + input: '0.0000015', + output: '0.000002', + }, + }, + { + name: 'gpt-4-1106-preview - [Medium-High Intelligence, Medium Cost, Standard Speed]', + value: 'gpt-4-1106-preview', + pricing: { + input: '0.00001', + output: '0.00003', + }, + }, + { + name: 'gpt-3.5-turbo-1106 - [Medium Intelligence, Low Cost, Fast Speed]', + value: 'gpt-3.5-turbo-1106', + pricing: { + input: '0.000003', + output: '0.000004', + }, + }, + { + name: 'gpt-4-0125-preview - [Medium-High Intelligence, Medium Cost, Standard Speed]', + value: 'gpt-4-0125-preview', + pricing: { + input: '0.00001', + output: '0.00003', + }, + }, + { + name: 'gpt-4-turbo-preview - [Medium-High Intelligence, Medium Cost, Fast Speed]', + value: 'gpt-4-turbo-preview', + pricing: { + input: '0.00001', + output: '0.00003', + }, + }, + { + name: 'gpt-3.5-turbo-0125 - [Medium Intelligence, Low Cost, Fast Speed]', + value: 'gpt-3.5-turbo-0125', + pricing: { + input: '0.000003', + output: '0.000004', + }, + }, + { + name: 'gpt-4-turbo - [Medium-High Intelligence, Medium Cost, Fast Speed]', + value: 'gpt-4-turbo', + pricing: { + input: '0.00001', + output: '0.00003', + }, + }, + { + name: 'gpt-4-turbo-2024-04-09 - [Medium-High Intelligence, Medium Cost, Fast Speed]', + value: 'gpt-4-turbo-2024-04-09', + pricing: { + input: '0.00001', + output: '0.00003', + }, + }, + { + name: 'gpt-4o - [High Intelligence, Medium Cost, Standard Speed]', + value: 'gpt-4o', + pricing: { + input: '0.0000025', + output: '0.00001', + }, + }, + { + name: 'gpt-4o-mini - [Medium-High Intelligence, Low Cost, Fast Speed]', + value: 'gpt-4o-mini', + pricing: { + input: '0.00000015', + output: '0.0000006', + }, + }, + { + name: 'o1 - [High Intelligence, High Cost, Standard Speed, Reasoning]', + value: 'o1', + pricing: { + input: '0.00015', + output: '0.0006', + }, + }, + { + name: 'o3-mini - [Medium-High Intelligence, Medium Cost, Fast Speed, Reasoning]', + value: 'o3-mini', + pricing: { + input: '0.0000011', + output: '0.0000044', + }, + }, + { + name: 'o1-pro - [High Intelligence, High Cost, Standard Speed, Reasoning]', + value: 'o1-pro', + pricing: { + input: '0.00015', + output: '0.0006', + }, + }, + { + name: 'o3 - [High Intelligence, High Cost, Standard Speed, Reasoning]', + value: 'o3', + pricing: { + input: '0.00001', + output: '0.00004', + }, + }, + { + name: 'o4-mini - [Medium-High Intelligence, Medium Cost, Fast Speed, Reasoning]', + value: 'o4-mini', + pricing: { + input: '0.000002', + output: '0.000008', + }, + }, + { + name: 'gpt-4.1 - [Medium Intelligence, High Cost, Standard Speed]', + value: 'gpt-4.1', + pricing: { + input: '0.000002', + output: '0.000008', + }, + }, + { + name: 'gpt-4.1-mini - [Medium Intelligence, Low Cost, Fast Speed]', + value: 'gpt-4.1-mini', + pricing: { + input: '0.0000004', + output: '0.0000016', + }, + }, + { + name: 'gpt-4.1-nano - [Low Intelligence, Low Cost, Fast Speed]', + value: 'gpt-4.1-nano', + pricing: { + input: '0.0000001', + output: '0.0000004', + }, + }, + { + name: 'o3-pro - [High Intelligence, High Cost, Standard Speed, Reasoning]', + value: 'o3-pro', + pricing: { + input: '0.00002', + output: '0.00008', + }, + }, + { + name: 'gpt-5 - [High Intelligence, High Cost, Standard Speed]', + value: 'gpt-5', + pricing: { + input: '0.00000025', + output: '0.000002', + }, + }, + { + name: 'gpt-5-mini - [Medium-High Intelligence, Medium Cost, Fast Speed]', + value: 'gpt-5-mini', + pricing: { + input: '0.00000025', + output: '0.000002', + }, + }, + { + name: 'gpt-5-nano - [Medium-High Intelligence, High Cost, Fast Speed]', + value: 'gpt-5-nano', + pricing: { + input: '0.00000005', + output: '0.0000004', + }, + }, + { + name: 'gpt-5-codex - [High Intelligence, High Cost, Standard Speed]', + value: 'gpt-5-codex', + pricing: { + input: '0.00000125', + output: '0.00001', + }, + }, + { + name: 'gpt-5-pro - [High Intelligence, High Cost, Standard Speed]', + value: 'gpt-5-pro', + pricing: { + input: '0.000015', + output: '0.00012', + }, + }, + ], + google: [ + { + name: 'Gemini 2.5 Flash - [Medium Intelligence, Low Cost, Fast Speed]', + value: 'models/gemini-2.5-flash', + pricing: { + input: '0.0000003', + output: '0.0000025', + }, + }, + { + name: 'Gemini 2.5 Pro - [Medium-High Intelligence, Medium Cost, Fast Speed]', + value: 'models/gemini-2.5-pro', + pricing: { + input: '0.00000125', + output: '0.00001', + }, + }, + { + name: 'Gemini 2.5 Flash-Lite - [Medium Intelligence, Low Cost, Fast Speed]', + value: 'models/gemini-2.5-flash-lite', + pricing: { + input: '0.0000001', + output: '0.0000004', + }, + }, + { + name: 'Gemini 3 Pro Preview - [Medium-High Intelligence, Medium Cost, Fast Speed]', + value: 'models/gemini-3-pro-preview', + pricing: { + input: '0.000002', + output: '0.000012', + }, + }, + ], + xai: [ + { + name: 'grok-2-1212 - [Medium Intelligence, Medium Cost, Standard Speed]', + value: 'grok-2-1212', + pricing: { + input: '0', + output: '0', + }, + }, + { + name: 'grok-3 - [High Intelligence, Medium Cost, Standard Speed]', + value: 'grok-3', + pricing: { + input: '0.000003', + output: '0.000015', + }, + }, + { + name: 'grok-3-mini - [Medium-High Intelligence, Low Cost, Fast Speed]', + value: 'grok-3-mini', + pricing: { + input: '0.0000003', + output: '0.0000005', + }, + }, + { + name: 'Grok 4 (July 2025) - [High Intelligence, Medium Cost, Standard Speed]', + value: 'grok-4-0709', + pricing: { + input: '0.000003', + output: '0.000015', + }, + }, + { + name: 'Grok 4.1 Fast - [High Intelligence, Medium Cost, Fast Speed, Reasoning]', + value: 'grok-4-1-fast-non-reasoning', + pricing: { + input: '0.0000002', + output: '0.0000005', + }, + }, + { + name: 'Grok 4.1 Fast Reasoning - [High Intelligence, Medium Cost, Fast Speed, Reasoning]', + value: 'grok-4-1-fast-reasoning', + pricing: { + input: '0.0000002', + output: '0.0000005', + }, + }, + { + name: 'Grok 4 Fast - [High Intelligence, Medium Cost, Fast Speed, Reasoning]', + value: 'grok-4-fast-non-reasoning', + pricing: { + input: '0.0000002', + output: '0.0000005', + }, + }, + { + name: 'Grok 4 Fast Reasoning - [High Intelligence, Medium Cost, Fast Speed, Reasoning]', + value: 'grok-4-fast-reasoning', + pricing: { + input: '0.0000002', + output: '0.0000005', + }, + }, + { + name: 'Grok Code Fast 1 - [Medium Intelligence, Medium Cost, Fast Speed]', + value: 'grok-code-fast-1', + pricing: { + input: '0.0000002', + output: '0.0000015', + }, + }, + ], +} as const; diff --git a/src/types/model.types.ts b/src/types/model.types.ts new file mode 100644 index 0000000..0e3b9df --- /dev/null +++ b/src/types/model.types.ts @@ -0,0 +1,48 @@ +import { PROVIDER_MODELS } from '../constants/provider-models'; + +export interface GenericModel { + name: string; + value: string; + pricing: { + input: string; + output: string; + }; +} + +export type LLMProvider = keyof typeof PROVIDER_MODELS; +export type LLMProviderModels = (typeof PROVIDER_MODELS)[LLMProvider][number]['value']; +export type Models = { [key in LLMProvider]: GenericModel[] }; + +export interface OllamaModel { + name: string; + model: string; +} + +export interface LMStudioModel { + id: string; +} + +export interface GroqModel { + id: string; +} + +export interface AnthropicModel { + id: string; + display_name: string; +} + +export interface OpenAIModel { + id: string; +} + +export interface GoogleModel { + name: string; + displayName: string; + baseModelId: string; + inputTokenLimit: number; + outputTokenLimit: number; +} + +export interface XAIModel { + id: string; +} diff --git a/src/utils/get-models.ts b/src/utils/get-models.ts index cfd3a0a..42840bf 100644 --- a/src/utils/get-models.ts +++ b/src/utils/get-models.ts @@ -1,13 +1,6 @@ import { AppConfig } from 'config/config.interface'; - -interface GenericModel { - name: string; - value: string; - inputTokenLimit?: number; - outputTokenLimit?: number; - pricePerInputToken?: number; - pricePerOutputToken?: number; -} +import { GenericModel, LLMProvider, LMStudioModel, OllamaModel } from 'types/model.types'; +import { PROVIDER_MODELS } from '../constants/provider-models'; export async function getModels(config: AppConfig): Promise { const provider = config.llm.provider; @@ -26,31 +19,14 @@ export async function getModels(config: AppConfig): Promise { case 'lm-studio': models = await getLMStudioModels(baseUrl); break; - case 'groq': - models = await getGroqModels(config.apiKeys.groq); - break; - case 'anthropic': - models = await getAnthropicModels(config.apiKeys.anthropic); - break; - case 'openai': - models = await getOpenAIModels(config.apiKeys.openai); - break; - case 'google': - models = await getGoogleModels(config.apiKeys.google); - break; - case 'xai': - models = await getXAIModels(config.apiKeys.xai); + default: { + models = (await getProviderModels(provider)) as GenericModel[]; break; + } } - return models; } -interface OllamaModel { - name: string; - model: string; -} - async function getOllamaModels(baseUrl: string | undefined): Promise { const response = await fetch(`${baseUrl}/api/tags`); @@ -62,13 +38,13 @@ async function getOllamaModels(baseUrl: string | undefined): Promise ({ name: model.name, value: model.model, + pricing: { + input: '0', + output: '0', + }, })); } -interface LMStudioModel { - id: string; -} - async function getLMStudioModels(baseUrl: string | undefined): Promise { const response = await fetch(`${baseUrl}/models`); @@ -76,115 +52,16 @@ async function getLMStudioModels(baseUrl: string | undefined): Promise ({ name: model.id, value: model.id, - })); -} - -interface GroqModel { - id: string; -} - -async function getGroqModels(apiKey: string): Promise { - const response = await fetch('https://api.groq.com/openai/v1/models', { - headers: { - Authorization: `Bearer ${apiKey}`, + pricing: { + input: '0', + output: '0', }, - }); - if (!response.ok) { - throw new Error(`Failed to fetch models from Groq`); - } - const data = (await response.json()) as { data: GroqModel[] }; - return data.data.map((model) => ({ - name: model.id, - value: model.id, })); } -interface AnthropicModel { - id: string; - display_name: string; -} - -async function getAnthropicModels(apiKey: string): Promise { - const response = await fetch('https://api.anthropic.com/v1/models', { - headers: { - 'X-Api-Key': apiKey, - 'anthropic-version': '2023-06-01', - }, - }); - if (!response.ok) { - throw new Error(`Failed to fetch models from Anthropic`); - } - - const data = (await response.json()) as { data: AnthropicModel[] }; - - return data.data.map((model) => ({ - name: model.display_name, - value: model.id, - })); -} - -interface OpenAIModel { - id: string; -} - -async function getOpenAIModels(apiKey: string): Promise { - const response = await fetch('https://api.openai.com/v1/models', { - headers: { - Authorization: `Bearer ${apiKey}`, - }, - }); - if (!response.ok) { - throw new Error(`Failed to fetch models from OpenAI`); - } - const data = (await response.json()) as { data: OpenAIModel[] }; - return data.data.map((model) => ({ - name: model.id, - value: model.id, - })); -} - -interface GoogleModel { - name: string; - displayName: string; - baseModelId: string; - inputTokenLimit: number; - outputTokenLimit: number; -} -async function getGoogleModels(apiKey: string): Promise { - const response = await fetch('https://generativelanguage.googleapis.com/v1beta/models', { - headers: { - 'x-goog-api-key': apiKey, - }, - }); - if (!response.ok) { - throw new Error(`Failed to fetch models from Google`); - } - const data = (await response.json()) as { models: GoogleModel[] }; - return data.models.map((model) => ({ - name: model.displayName, - value: model.name, - })); -} - -interface XAIModel { - id: string; -} -async function getXAIModels(apiKey: string): Promise { - const response = await fetch('https://api.x.ai/v1/models', { - headers: { - Authorization: `Bearer ${apiKey}`, - }, - }); - if (!response.ok) { - throw new Error(`Failed to fetch models from XAI`); - } - const data = (await response.json()) as { data: XAIModel[] }; - return data.data.map((model) => ({ - name: model.id, - value: model.id, - })); +async function getProviderModels(provider: LLMProvider): Promise { + return PROVIDER_MODELS[provider]; }