fix: providers config for azure, bedrock, and vertex (#822)
* fix: providers config for azure, bedrock, and vertex * chore: improve changelog * chore: fix CI
This commit is contained in:
@@ -11,6 +11,7 @@ import fs from 'fs';
|
||||
import https from 'https';
|
||||
import http from 'http';
|
||||
import inquirer from 'inquirer';
|
||||
import search from '@inquirer/search';
|
||||
import ora from 'ora'; // Import ora
|
||||
|
||||
import {
|
||||
@@ -71,6 +72,8 @@ import {
|
||||
getBaseUrlForRole
|
||||
} from './config-manager.js';
|
||||
|
||||
import { CUSTOM_PROVIDERS } from '../../src/constants/providers.js';
|
||||
|
||||
import {
|
||||
COMPLEXITY_REPORT_FILE,
|
||||
PRD_FILE,
|
||||
@@ -306,6 +309,16 @@ async function runInteractiveSetup(projectRoot) {
|
||||
value: '__CUSTOM_BEDROCK__'
|
||||
};
|
||||
|
||||
const customAzureOption = {
|
||||
name: '* Custom Azure OpenAI model', // Add Azure custom option
|
||||
value: '__CUSTOM_AZURE__'
|
||||
};
|
||||
|
||||
const customVertexOption = {
|
||||
name: '* Custom Vertex AI model', // Add Vertex custom option
|
||||
value: '__CUSTOM_VERTEX__'
|
||||
};
|
||||
|
||||
let choices = [];
|
||||
let defaultIndex = 0; // Default to 'Cancel'
|
||||
|
||||
@@ -344,43 +357,50 @@ async function runInteractiveSetup(projectRoot) {
|
||||
);
|
||||
}
|
||||
|
||||
// Construct final choices list based on whether 'None' is allowed
|
||||
const commonPrefix = [];
|
||||
// Construct final choices list with custom options moved to bottom
|
||||
const systemOptions = [];
|
||||
if (noChangeOption) {
|
||||
commonPrefix.push(noChangeOption);
|
||||
systemOptions.push(noChangeOption);
|
||||
}
|
||||
commonPrefix.push(cancelOption);
|
||||
commonPrefix.push(customOpenRouterOption);
|
||||
commonPrefix.push(customOllamaOption);
|
||||
commonPrefix.push(customBedrockOption);
|
||||
systemOptions.push(cancelOption);
|
||||
|
||||
const prefixLength = commonPrefix.length; // Initial prefix length
|
||||
const customOptions = [
|
||||
customOpenRouterOption,
|
||||
customOllamaOption,
|
||||
customBedrockOption,
|
||||
customAzureOption,
|
||||
customVertexOption
|
||||
];
|
||||
|
||||
const systemLength = systemOptions.length;
|
||||
|
||||
if (allowNone) {
|
||||
choices = [
|
||||
...commonPrefix,
|
||||
new inquirer.Separator(),
|
||||
{ name: '⚪ None (disable)', value: null }, // Symbol updated
|
||||
new inquirer.Separator(),
|
||||
...roleChoices
|
||||
...systemOptions,
|
||||
new inquirer.Separator('── Standard Models ──'),
|
||||
{ name: '⚪ None (disable)', value: null },
|
||||
...roleChoices,
|
||||
new inquirer.Separator('── Custom Providers ──'),
|
||||
...customOptions
|
||||
];
|
||||
// Adjust default index: Prefix + Sep1 + None + Sep2 (+3)
|
||||
const noneOptionIndex = prefixLength + 1;
|
||||
// Adjust default index: System + Sep1 + None (+2)
|
||||
const noneOptionIndex = systemLength + 1;
|
||||
defaultIndex =
|
||||
currentChoiceIndex !== -1
|
||||
? currentChoiceIndex + prefixLength + 3 // Offset by prefix and separators
|
||||
? currentChoiceIndex + systemLength + 2 // Offset by system options and separators
|
||||
: noneOptionIndex; // Default to 'None' if no current model matched
|
||||
} else {
|
||||
choices = [
|
||||
...commonPrefix,
|
||||
new inquirer.Separator(),
|
||||
...systemOptions,
|
||||
new inquirer.Separator('── Standard Models ──'),
|
||||
...roleChoices,
|
||||
new inquirer.Separator()
|
||||
new inquirer.Separator('── Custom Providers ──'),
|
||||
...customOptions
|
||||
];
|
||||
// Adjust default index: Prefix + Sep (+1)
|
||||
// Adjust default index: System + Sep (+1)
|
||||
defaultIndex =
|
||||
currentChoiceIndex !== -1
|
||||
? currentChoiceIndex + prefixLength + 1 // Offset by prefix and separator
|
||||
? currentChoiceIndex + systemLength + 1 // Offset by system options and separator
|
||||
: noChangeOption
|
||||
? 1
|
||||
: 0; // Default to 'No Change' if present, else 'Cancel'
|
||||
@@ -403,32 +423,63 @@ async function runInteractiveSetup(projectRoot) {
|
||||
const researchPromptData = getPromptData('research');
|
||||
const fallbackPromptData = getPromptData('fallback', true); // Allow 'None' for fallback
|
||||
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'mainModel',
|
||||
message: 'Select the main model for generation/updates:',
|
||||
choices: mainPromptData.choices,
|
||||
default: mainPromptData.default
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'researchModel',
|
||||
// Display helpful intro message
|
||||
console.log(chalk.cyan('\n🎯 Interactive Model Setup'));
|
||||
console.log(chalk.gray('━'.repeat(50)));
|
||||
console.log(chalk.yellow('💡 Navigation tips:'));
|
||||
console.log(chalk.gray(' • Type to search and filter options'));
|
||||
console.log(chalk.gray(' • Use ↑↓ arrow keys to navigate results'));
|
||||
console.log(
|
||||
chalk.gray(
|
||||
' • Standard models are listed first, custom providers at bottom'
|
||||
)
|
||||
);
|
||||
console.log(chalk.gray(' • Press Enter to select\n'));
|
||||
|
||||
// Helper function to create search source for models
|
||||
const createSearchSource = (choices, defaultValue) => {
|
||||
return (searchTerm = '') => {
|
||||
const filteredChoices = choices.filter((choice) => {
|
||||
if (choice.type === 'separator') return true; // Always show separators
|
||||
const searchText = choice.name || '';
|
||||
return searchText.toLowerCase().includes(searchTerm.toLowerCase());
|
||||
});
|
||||
return Promise.resolve(filteredChoices);
|
||||
};
|
||||
};
|
||||
|
||||
const answers = {};
|
||||
|
||||
// Main model selection
|
||||
answers.mainModel = await search({
|
||||
message: 'Select the main model for generation/updates:',
|
||||
source: createSearchSource(mainPromptData.choices, mainPromptData.default),
|
||||
pageSize: 15
|
||||
});
|
||||
|
||||
if (answers.mainModel !== '__CANCEL__') {
|
||||
// Research model selection
|
||||
answers.researchModel = await search({
|
||||
message: 'Select the research model:',
|
||||
choices: researchPromptData.choices,
|
||||
default: researchPromptData.default,
|
||||
when: (ans) => ans.mainModel !== '__CANCEL__'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'fallbackModel',
|
||||
message: 'Select the fallback model (optional):',
|
||||
choices: fallbackPromptData.choices,
|
||||
default: fallbackPromptData.default,
|
||||
when: (ans) =>
|
||||
ans.mainModel !== '__CANCEL__' && ans.researchModel !== '__CANCEL__'
|
||||
source: createSearchSource(
|
||||
researchPromptData.choices,
|
||||
researchPromptData.default
|
||||
),
|
||||
pageSize: 15
|
||||
});
|
||||
|
||||
if (answers.researchModel !== '__CANCEL__') {
|
||||
// Fallback model selection
|
||||
answers.fallbackModel = await search({
|
||||
message: 'Select the fallback model (optional):',
|
||||
source: createSearchSource(
|
||||
fallbackPromptData.choices,
|
||||
fallbackPromptData.default
|
||||
),
|
||||
pageSize: 15
|
||||
});
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
let setupSuccess = true;
|
||||
let setupConfigModified = false;
|
||||
@@ -468,7 +519,7 @@ async function runInteractiveSetup(projectRoot) {
|
||||
return true; // Continue setup, but don't set this role
|
||||
}
|
||||
modelIdToSet = customId;
|
||||
providerHint = 'openrouter';
|
||||
providerHint = CUSTOM_PROVIDERS.OPENROUTER;
|
||||
// Validate against live OpenRouter list
|
||||
const openRouterModels = await fetchOpenRouterModelsCLI();
|
||||
if (
|
||||
@@ -497,7 +548,7 @@ async function runInteractiveSetup(projectRoot) {
|
||||
return true; // Continue setup, but don't set this role
|
||||
}
|
||||
modelIdToSet = customId;
|
||||
providerHint = 'ollama';
|
||||
providerHint = CUSTOM_PROVIDERS.OLLAMA;
|
||||
// Get the Ollama base URL from config for this role
|
||||
const ollamaBaseURL = getBaseUrlForRole(role, projectRoot);
|
||||
// Validate against live Ollama list
|
||||
@@ -538,7 +589,7 @@ async function runInteractiveSetup(projectRoot) {
|
||||
return true; // Continue setup, but don't set this role
|
||||
}
|
||||
modelIdToSet = customId;
|
||||
providerHint = 'bedrock';
|
||||
providerHint = CUSTOM_PROVIDERS.BEDROCK;
|
||||
|
||||
// Check if AWS environment variables exist
|
||||
if (
|
||||
@@ -559,6 +610,76 @@ async function runInteractiveSetup(projectRoot) {
|
||||
`Custom Bedrock model "${modelIdToSet}" will be used. No validation performed.`
|
||||
)
|
||||
);
|
||||
} else if (selectedValue === '__CUSTOM_AZURE__') {
|
||||
isCustomSelection = true;
|
||||
const { customId } = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'customId',
|
||||
message: `Enter the custom Azure OpenAI Model ID for the ${role} role (e.g., gpt-4o):`
|
||||
}
|
||||
]);
|
||||
if (!customId) {
|
||||
console.log(chalk.yellow('No custom ID entered. Skipping role.'));
|
||||
return true; // Continue setup, but don't set this role
|
||||
}
|
||||
modelIdToSet = customId;
|
||||
providerHint = CUSTOM_PROVIDERS.AZURE;
|
||||
|
||||
// Check if Azure environment variables exist
|
||||
if (
|
||||
!process.env.AZURE_OPENAI_API_KEY ||
|
||||
!process.env.AZURE_OPENAI_ENDPOINT
|
||||
) {
|
||||
console.error(
|
||||
chalk.red(
|
||||
'Error: AZURE_OPENAI_API_KEY and/or AZURE_OPENAI_ENDPOINT environment variables are missing. Please set them before using custom Azure models.'
|
||||
)
|
||||
);
|
||||
setupSuccess = false;
|
||||
return true; // Continue setup, but mark as failed
|
||||
}
|
||||
|
||||
console.log(
|
||||
chalk.blue(
|
||||
`Custom Azure OpenAI model "${modelIdToSet}" will be used. No validation performed.`
|
||||
)
|
||||
);
|
||||
} else if (selectedValue === '__CUSTOM_VERTEX__') {
|
||||
isCustomSelection = true;
|
||||
const { customId } = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'customId',
|
||||
message: `Enter the custom Vertex AI Model ID for the ${role} role (e.g., gemini-1.5-pro-002):`
|
||||
}
|
||||
]);
|
||||
if (!customId) {
|
||||
console.log(chalk.yellow('No custom ID entered. Skipping role.'));
|
||||
return true; // Continue setup, but don't set this role
|
||||
}
|
||||
modelIdToSet = customId;
|
||||
providerHint = CUSTOM_PROVIDERS.VERTEX;
|
||||
|
||||
// Check if Google/Vertex environment variables exist
|
||||
if (
|
||||
!process.env.GOOGLE_API_KEY &&
|
||||
!process.env.GOOGLE_APPLICATION_CREDENTIALS
|
||||
) {
|
||||
console.error(
|
||||
chalk.red(
|
||||
'Error: Either GOOGLE_API_KEY or GOOGLE_APPLICATION_CREDENTIALS environment variable is required. Please set one before using custom Vertex models.'
|
||||
)
|
||||
);
|
||||
setupSuccess = false;
|
||||
return true; // Continue setup, but mark as failed
|
||||
}
|
||||
|
||||
console.log(
|
||||
chalk.blue(
|
||||
`Custom Vertex AI model "${modelIdToSet}" will be used. No validation performed.`
|
||||
)
|
||||
);
|
||||
} else if (
|
||||
selectedValue &&
|
||||
typeof selectedValue === 'object' &&
|
||||
@@ -3307,6 +3428,14 @@ ${result.result}
|
||||
'--bedrock',
|
||||
'Allow setting a custom Bedrock model ID (use with --set-*) '
|
||||
)
|
||||
.option(
|
||||
'--azure',
|
||||
'Allow setting a custom Azure OpenAI model ID (use with --set-*) '
|
||||
)
|
||||
.option(
|
||||
'--vertex',
|
||||
'Allow setting a custom Vertex AI model ID (use with --set-*) '
|
||||
)
|
||||
.addHelpText(
|
||||
'after',
|
||||
`
|
||||
@@ -3318,6 +3447,8 @@ Examples:
|
||||
$ task-master models --set-main my-custom-model --ollama # Set custom Ollama model for main role
|
||||
$ task-master models --set-main anthropic.claude-3-sonnet-20240229-v1:0 --bedrock # Set custom Bedrock model for main role
|
||||
$ task-master models --set-main some/other-model --openrouter # Set custom OpenRouter model for main role
|
||||
$ task-master models --set-main gpt-4o --azure # Set custom Azure OpenAI model for main role
|
||||
$ task-master models --set-main claude-3-5-sonnet@20241022 --vertex # Set custom Vertex AI model for main role
|
||||
$ task-master models --setup # Run interactive setup`
|
||||
)
|
||||
.action(async (options) => {
|
||||
|
||||
@@ -5,6 +5,12 @@ import { fileURLToPath } from 'url';
|
||||
import { log, findProjectRoot, resolveEnvVariable } from './utils.js';
|
||||
import { LEGACY_CONFIG_FILE } from '../../src/constants/paths.js';
|
||||
import { findConfigPath } from '../../src/utils/path-utils.js';
|
||||
import {
|
||||
VALIDATED_PROVIDERS,
|
||||
CUSTOM_PROVIDERS,
|
||||
CUSTOM_PROVIDERS_ARRAY,
|
||||
ALL_PROVIDERS
|
||||
} from '../../src/constants/providers.js';
|
||||
|
||||
// Calculate __dirname in ESM
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
@@ -29,9 +35,6 @@ try {
|
||||
process.exit(1); // Exit if models can't be loaded
|
||||
}
|
||||
|
||||
// Define valid providers dynamically from the loaded MODEL_MAP
|
||||
const VALID_PROVIDERS = Object.keys(MODEL_MAP || {});
|
||||
|
||||
// Default configuration values (used if config file is missing or incomplete)
|
||||
const DEFAULTS = {
|
||||
models: {
|
||||
@@ -233,12 +236,25 @@ function getConfig(explicitRoot = null, forceReload = false) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if a provider name is in the list of supported providers.
|
||||
* Validates if a provider name is supported.
|
||||
* Custom providers (azure, vertex, bedrock, openrouter, ollama) are always allowed.
|
||||
* Validated providers must exist in the MODEL_MAP from supported-models.json.
|
||||
* @param {string} providerName The name of the provider.
|
||||
* @returns {boolean} True if the provider is valid, false otherwise.
|
||||
*/
|
||||
function validateProvider(providerName) {
|
||||
return VALID_PROVIDERS.includes(providerName);
|
||||
// Custom providers are always allowed
|
||||
if (CUSTOM_PROVIDERS_ARRAY.includes(providerName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validated providers must exist in MODEL_MAP
|
||||
if (VALIDATED_PROVIDERS.includes(providerName)) {
|
||||
return !!(MODEL_MAP && MODEL_MAP[providerName]);
|
||||
}
|
||||
|
||||
// Unknown providers are not allowed
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -736,11 +752,11 @@ function getUserId(explicitRoot = null) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of all provider names defined in the MODEL_MAP.
|
||||
* @returns {string[]} An array of provider names.
|
||||
* Gets a list of all known provider names (both validated and custom).
|
||||
* @returns {string[]} An array of all provider names.
|
||||
*/
|
||||
function getAllProviders() {
|
||||
return Object.keys(MODEL_MAP || {});
|
||||
return ALL_PROVIDERS;
|
||||
}
|
||||
|
||||
function getBaseUrlForRole(role, explicitRoot = null) {
|
||||
@@ -759,7 +775,9 @@ export {
|
||||
// Validation
|
||||
validateProvider,
|
||||
validateProviderModelCombination,
|
||||
VALID_PROVIDERS,
|
||||
VALIDATED_PROVIDERS,
|
||||
CUSTOM_PROVIDERS,
|
||||
ALL_PROVIDERS,
|
||||
MODEL_MAP,
|
||||
getAvailableModels,
|
||||
// Role-specific getters (No env var overrides)
|
||||
|
||||
@@ -3,28 +3,40 @@
|
||||
{
|
||||
"id": "claude-sonnet-4-20250514",
|
||||
"swe_score": 0.727,
|
||||
"cost_per_1m_tokens": { "input": 3.0, "output": 15.0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 3.0,
|
||||
"output": 15.0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 64000
|
||||
},
|
||||
{
|
||||
"id": "claude-opus-4-20250514",
|
||||
"swe_score": 0.725,
|
||||
"cost_per_1m_tokens": { "input": 15.0, "output": 75.0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 15.0,
|
||||
"output": 75.0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 32000
|
||||
},
|
||||
{
|
||||
"id": "claude-3-7-sonnet-20250219",
|
||||
"swe_score": 0.623,
|
||||
"cost_per_1m_tokens": { "input": 3.0, "output": 15.0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 3.0,
|
||||
"output": 15.0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 120000
|
||||
},
|
||||
{
|
||||
"id": "claude-3-5-sonnet-20241022",
|
||||
"swe_score": 0.49,
|
||||
"cost_per_1m_tokens": { "input": 3.0, "output": 15.0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 3.0,
|
||||
"output": 15.0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 64000
|
||||
}
|
||||
@@ -33,81 +45,120 @@
|
||||
{
|
||||
"id": "gpt-4o",
|
||||
"swe_score": 0.332,
|
||||
"cost_per_1m_tokens": { "input": 2.5, "output": 10.0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 2.5,
|
||||
"output": 10.0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 16384
|
||||
},
|
||||
{
|
||||
"id": "o1",
|
||||
"swe_score": 0.489,
|
||||
"cost_per_1m_tokens": { "input": 15.0, "output": 60.0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 15.0,
|
||||
"output": 60.0
|
||||
},
|
||||
"allowed_roles": ["main"]
|
||||
},
|
||||
{
|
||||
"id": "o3",
|
||||
"swe_score": 0.5,
|
||||
"cost_per_1m_tokens": { "input": 2.0, "output": 8.0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 2.0,
|
||||
"output": 8.0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "o3-mini",
|
||||
"swe_score": 0.493,
|
||||
"cost_per_1m_tokens": { "input": 1.1, "output": 4.4 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1.1,
|
||||
"output": 4.4
|
||||
},
|
||||
"allowed_roles": ["main"],
|
||||
"max_tokens": 100000
|
||||
},
|
||||
{
|
||||
"id": "o4-mini",
|
||||
"swe_score": 0.45,
|
||||
"cost_per_1m_tokens": { "input": 1.1, "output": 4.4 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1.1,
|
||||
"output": 4.4
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "o1-mini",
|
||||
"swe_score": 0.4,
|
||||
"cost_per_1m_tokens": { "input": 1.1, "output": 4.4 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1.1,
|
||||
"output": 4.4
|
||||
},
|
||||
"allowed_roles": ["main"]
|
||||
},
|
||||
{
|
||||
"id": "o1-pro",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 150.0, "output": 600.0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 150.0,
|
||||
"output": 600.0
|
||||
},
|
||||
"allowed_roles": ["main"]
|
||||
},
|
||||
{
|
||||
"id": "gpt-4-5-preview",
|
||||
"swe_score": 0.38,
|
||||
"cost_per_1m_tokens": { "input": 75.0, "output": 150.0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 75.0,
|
||||
"output": 150.0
|
||||
},
|
||||
"allowed_roles": ["main"]
|
||||
},
|
||||
{
|
||||
"id": "gpt-4-1-mini",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.4, "output": 1.6 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.4,
|
||||
"output": 1.6
|
||||
},
|
||||
"allowed_roles": ["main"]
|
||||
},
|
||||
{
|
||||
"id": "gpt-4-1-nano",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.1, "output": 0.4 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.1,
|
||||
"output": 0.4
|
||||
},
|
||||
"allowed_roles": ["main"]
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini",
|
||||
"swe_score": 0.3,
|
||||
"cost_per_1m_tokens": { "input": 0.15, "output": 0.6 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.15,
|
||||
"output": 0.6
|
||||
},
|
||||
"allowed_roles": ["main"]
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-search-preview",
|
||||
"swe_score": 0.33,
|
||||
"cost_per_1m_tokens": { "input": 2.5, "output": 10.0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 2.5,
|
||||
"output": 10.0
|
||||
},
|
||||
"allowed_roles": ["research"]
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini-search-preview",
|
||||
"swe_score": 0.3,
|
||||
"cost_per_1m_tokens": { "input": 0.15, "output": 0.6 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.15,
|
||||
"output": 0.6
|
||||
},
|
||||
"allowed_roles": ["research"]
|
||||
}
|
||||
],
|
||||
@@ -136,7 +187,10 @@
|
||||
{
|
||||
"id": "gemini-2.0-flash",
|
||||
"swe_score": 0.518,
|
||||
"cost_per_1m_tokens": { "input": 0.15, "output": 0.6 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.15,
|
||||
"output": 0.6
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1048000
|
||||
},
|
||||
@@ -152,35 +206,50 @@
|
||||
{
|
||||
"id": "sonar-pro",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 3, "output": 15 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 3,
|
||||
"output": 15
|
||||
},
|
||||
"allowed_roles": ["main", "research"],
|
||||
"max_tokens": 8700
|
||||
},
|
||||
{
|
||||
"id": "sonar",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 1, "output": 1 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1,
|
||||
"output": 1
|
||||
},
|
||||
"allowed_roles": ["research"],
|
||||
"max_tokens": 8700
|
||||
},
|
||||
{
|
||||
"id": "deep-research",
|
||||
"swe_score": 0.211,
|
||||
"cost_per_1m_tokens": { "input": 2, "output": 8 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 2,
|
||||
"output": 8
|
||||
},
|
||||
"allowed_roles": ["research"],
|
||||
"max_tokens": 8700
|
||||
},
|
||||
{
|
||||
"id": "sonar-reasoning-pro",
|
||||
"swe_score": 0.211,
|
||||
"cost_per_1m_tokens": { "input": 2, "output": 8 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 2,
|
||||
"output": 8
|
||||
},
|
||||
"allowed_roles": ["main", "research", "fallback"],
|
||||
"max_tokens": 8700
|
||||
},
|
||||
{
|
||||
"id": "sonar-reasoning",
|
||||
"swe_score": 0.211,
|
||||
"cost_per_1m_tokens": { "input": 1, "output": 5 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1,
|
||||
"output": 5
|
||||
},
|
||||
"allowed_roles": ["main", "research", "fallback"],
|
||||
"max_tokens": 8700
|
||||
}
|
||||
@@ -190,7 +259,10 @@
|
||||
"id": "grok-3",
|
||||
"name": "Grok 3",
|
||||
"swe_score": null,
|
||||
"cost_per_1m_tokens": { "input": 3, "output": 15 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 3,
|
||||
"output": 15
|
||||
},
|
||||
"allowed_roles": ["main", "fallback", "research"],
|
||||
"max_tokens": 131072
|
||||
},
|
||||
@@ -198,7 +270,10 @@
|
||||
"id": "grok-3-fast",
|
||||
"name": "Grok 3 Fast",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 5, "output": 25 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 5,
|
||||
"output": 25
|
||||
},
|
||||
"allowed_roles": ["main", "fallback", "research"],
|
||||
"max_tokens": 131072
|
||||
}
|
||||
@@ -207,43 +282,64 @@
|
||||
{
|
||||
"id": "devstral:latest",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "qwen3:latest",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "qwen3:14b",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "qwen3:32b",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "mistral-small3.1:latest",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "llama3.3:latest",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "phi4:latest",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
}
|
||||
],
|
||||
@@ -251,175 +347,250 @@
|
||||
{
|
||||
"id": "google/gemini-2.5-flash-preview-05-20",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.15, "output": 0.6 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.15,
|
||||
"output": 0.6
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1048576
|
||||
},
|
||||
{
|
||||
"id": "google/gemini-2.5-flash-preview-05-20:thinking",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.15, "output": 3.5 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.15,
|
||||
"output": 3.5
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1048576
|
||||
},
|
||||
{
|
||||
"id": "google/gemini-2.5-pro-exp-03-25",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "deepseek/deepseek-chat-v3-0324:free",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 163840
|
||||
},
|
||||
{
|
||||
"id": "deepseek/deepseek-chat-v3-0324",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.27, "output": 1.1 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.27,
|
||||
"output": 1.1
|
||||
},
|
||||
"allowed_roles": ["main"],
|
||||
"max_tokens": 64000
|
||||
},
|
||||
{
|
||||
"id": "openai/gpt-4.1",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 2, "output": 8 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 2,
|
||||
"output": 8
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "openai/gpt-4.1-mini",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.4, "output": 1.6 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.4,
|
||||
"output": 1.6
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "openai/gpt-4.1-nano",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.1, "output": 0.4 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.1,
|
||||
"output": 0.4
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "openai/o3",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 10, "output": 40 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 10,
|
||||
"output": 40
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 200000
|
||||
},
|
||||
{
|
||||
"id": "openai/codex-mini",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 1.5, "output": 6 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1.5,
|
||||
"output": 6
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 100000
|
||||
},
|
||||
{
|
||||
"id": "openai/gpt-4o-mini",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.15, "output": 0.6 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.15,
|
||||
"output": 0.6
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 100000
|
||||
},
|
||||
{
|
||||
"id": "openai/o4-mini",
|
||||
"swe_score": 0.45,
|
||||
"cost_per_1m_tokens": { "input": 1.1, "output": 4.4 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1.1,
|
||||
"output": 4.4
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 100000
|
||||
},
|
||||
{
|
||||
"id": "openai/o4-mini-high",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 1.1, "output": 4.4 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1.1,
|
||||
"output": 4.4
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 100000
|
||||
},
|
||||
{
|
||||
"id": "openai/o1-pro",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 150, "output": 600 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 150,
|
||||
"output": 600
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 100000
|
||||
},
|
||||
{
|
||||
"id": "meta-llama/llama-3.3-70b-instruct",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 120, "output": 600 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 120,
|
||||
"output": 600
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1048576
|
||||
},
|
||||
{
|
||||
"id": "meta-llama/llama-4-maverick",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.18, "output": 0.6 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.18,
|
||||
"output": 0.6
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "meta-llama/llama-4-scout",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.08, "output": 0.3 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.08,
|
||||
"output": 0.3
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "qwen/qwen-max",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 1.6, "output": 6.4 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1.6,
|
||||
"output": 6.4
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 32768
|
||||
},
|
||||
{
|
||||
"id": "qwen/qwen-turbo",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.05, "output": 0.2 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.05,
|
||||
"output": 0.2
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "qwen/qwen3-235b-a22b",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.14, "output": 2 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.14,
|
||||
"output": 2
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 24000
|
||||
},
|
||||
{
|
||||
"id": "mistralai/mistral-small-3.1-24b-instruct:free",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 96000
|
||||
},
|
||||
{
|
||||
"id": "mistralai/mistral-small-3.1-24b-instruct",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.1, "output": 0.3 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.1,
|
||||
"output": 0.3
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 128000
|
||||
},
|
||||
{
|
||||
"id": "mistralai/devstral-small",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.1, "output": 0.3 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.1,
|
||||
"output": 0.3
|
||||
},
|
||||
"allowed_roles": ["main"],
|
||||
"max_tokens": 110000
|
||||
},
|
||||
{
|
||||
"id": "mistralai/mistral-nemo",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.03, "output": 0.07 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.03,
|
||||
"output": 0.07
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 100000
|
||||
},
|
||||
{
|
||||
"id": "thudm/glm-4-32b:free",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 32768
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
} from '../config-manager.js';
|
||||
import { findConfigPath } from '../../../src/utils/path-utils.js';
|
||||
import { log } from '../utils.js';
|
||||
import { CUSTOM_PROVIDERS } from '../../../src/constants/providers.js';
|
||||
|
||||
/**
|
||||
* Fetches the list of models from OpenRouter API.
|
||||
@@ -440,7 +441,7 @@ async function setModel(role, modelId, options = {}) {
|
||||
} else {
|
||||
// Either not found internally, OR found but under a DIFFERENT provider than hinted.
|
||||
// Proceed with custom logic based ONLY on the hint.
|
||||
if (providerHint === 'openrouter') {
|
||||
if (providerHint === CUSTOM_PROVIDERS.OPENROUTER) {
|
||||
// Check OpenRouter ONLY because hint was openrouter
|
||||
report('info', `Checking OpenRouter for ${modelId} (as hinted)...`);
|
||||
const openRouterModels = await fetchOpenRouterModels();
|
||||
@@ -449,7 +450,7 @@ async function setModel(role, modelId, options = {}) {
|
||||
openRouterModels &&
|
||||
openRouterModels.some((m) => m.id === modelId)
|
||||
) {
|
||||
determinedProvider = 'openrouter';
|
||||
determinedProvider = CUSTOM_PROVIDERS.OPENROUTER;
|
||||
|
||||
// Check if this is a free model (ends with :free)
|
||||
if (modelId.endsWith(':free')) {
|
||||
@@ -465,7 +466,7 @@ async function setModel(role, modelId, options = {}) {
|
||||
`Model ID "${modelId}" not found in the live OpenRouter model list. Please verify the ID and ensure it's available on OpenRouter.`
|
||||
);
|
||||
}
|
||||
} else if (providerHint === 'ollama') {
|
||||
} else if (providerHint === CUSTOM_PROVIDERS.OLLAMA) {
|
||||
// Check Ollama ONLY because hint was ollama
|
||||
report('info', `Checking Ollama for ${modelId} (as hinted)...`);
|
||||
|
||||
@@ -479,7 +480,7 @@ async function setModel(role, modelId, options = {}) {
|
||||
`Unable to connect to Ollama server at ${ollamaBaseURL}. Please ensure Ollama is running and try again.`
|
||||
);
|
||||
} else if (ollamaModels.some((m) => m.model === modelId)) {
|
||||
determinedProvider = 'ollama';
|
||||
determinedProvider = CUSTOM_PROVIDERS.OLLAMA;
|
||||
warningMessage = `Warning: Custom Ollama model '${modelId}' set. Ensure your Ollama server is running and has pulled this model. Taskmaster cannot guarantee compatibility.`;
|
||||
report('warn', warningMessage);
|
||||
} else {
|
||||
@@ -489,13 +490,23 @@ async function setModel(role, modelId, options = {}) {
|
||||
`Model ID "${modelId}" not found in the Ollama instance. Please verify the model is pulled and available. You can check available models with: curl ${tagsUrl}`
|
||||
);
|
||||
}
|
||||
} else if (providerHint === 'bedrock') {
|
||||
} else if (providerHint === CUSTOM_PROVIDERS.BEDROCK) {
|
||||
// Set provider without model validation since Bedrock models are managed by AWS
|
||||
determinedProvider = 'bedrock';
|
||||
determinedProvider = CUSTOM_PROVIDERS.BEDROCK;
|
||||
warningMessage = `Warning: Custom Bedrock model '${modelId}' set. Please ensure the model ID is valid and accessible in your AWS account.`;
|
||||
report('warn', warningMessage);
|
||||
} else if (providerHint === CUSTOM_PROVIDERS.AZURE) {
|
||||
// Set provider without model validation since Azure models are managed by Azure
|
||||
determinedProvider = CUSTOM_PROVIDERS.AZURE;
|
||||
warningMessage = `Warning: Custom Azure model '${modelId}' set. Please ensure the model deployment is valid and accessible in your Azure account.`;
|
||||
report('warn', warningMessage);
|
||||
} else if (providerHint === CUSTOM_PROVIDERS.VERTEX) {
|
||||
// Set provider without model validation since Vertex models are managed by Google Cloud
|
||||
determinedProvider = CUSTOM_PROVIDERS.VERTEX;
|
||||
warningMessage = `Warning: Custom Vertex AI model '${modelId}' set. Please ensure the model is valid and accessible in your Google Cloud project.`;
|
||||
report('warn', warningMessage);
|
||||
} else {
|
||||
// Invalid provider hint - should not happen
|
||||
// Invalid provider hint - should not happen with our constants
|
||||
throw new Error(`Invalid provider hint received: ${providerHint}`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user