From 59ee1e7baa36ef58b4ed71c092a870cc8c7e70fd Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 08:00:46 +0000 Subject: [PATCH] fix: support Azure provider with reasoning models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add azure, openrouter, bedrock, and ollama to VALIDATED_PROVIDERS array - Add Azure reasoning models (GPT-5, o1, o3, o3-mini, o4-mini) to supported-models.json - Implement automatic API endpoint detection for Azure reasoning models - Add dual endpoint support (chat/completions vs responses) in AzureProvider - Add smart URL adjustment logic for different Azure configurations - Maintain backward compatibility with existing Azure setups Fixes #638 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Ralph Khreish --- scripts/modules/supported-models.json | 61 +++++++++++++++++++++++++++ src/ai-providers/azure.js | 49 ++++++++++++++++++++- src/constants/providers.js | 6 ++- 3 files changed, 113 insertions(+), 3 deletions(-) diff --git a/scripts/modules/supported-models.json b/scripts/modules/supported-models.json index 2015d277..47674d3b 100644 --- a/scripts/modules/supported-models.json +++ b/scripts/modules/supported-models.json @@ -997,6 +997,67 @@ "allowed_roles": ["main", "fallback"], "max_tokens": 16384, "supported": true + }, + { + "id": "gpt-5", + "swe_score": 0.749, + "cost_per_1m_tokens": { + "input": 5.0, + "output": 20.0 + }, + "allowed_roles": ["main", "fallback"], + "max_tokens": 100000, + "temperature": 1, + "supported": true, + "api_type": "responses" + }, + { + "id": "o1", + "swe_score": 0.489, + "cost_per_1m_tokens": { + "input": 15.0, + "output": 60.0 + }, + "allowed_roles": ["main"], + "max_tokens": 100000, + "supported": true, + "api_type": "responses" + }, + { + "id": "o3", + "swe_score": 0.5, + "cost_per_1m_tokens": { + "input": 2.0, + "output": 8.0 + }, + "allowed_roles": ["main", "fallback"], + "max_tokens": 100000, + "supported": true, + "api_type": "responses" + }, + { + "id": "o3-mini", + "swe_score": 0.493, + "cost_per_1m_tokens": { + "input": 1.1, + "output": 4.4 + }, + "allowed_roles": ["main"], + "max_tokens": 100000, + "supported": true, + "api_type": "responses" + }, + { + "id": "o4-mini", + "swe_score": 0.45, + "cost_per_1m_tokens": { + "input": 1.1, + "output": 4.4 + }, + "allowed_roles": ["main", "fallback"], + "max_tokens": 100000, + "supported": true, + "api_type": "responses" } ], "bedrock": [ diff --git a/src/ai-providers/azure.js b/src/ai-providers/azure.js index 105e647b..9681ff77 100644 --- a/src/ai-providers/azure.js +++ b/src/ai-providers/azure.js @@ -5,6 +5,7 @@ import { createAzure } from '@ai-sdk/azure'; import { BaseAIProvider } from './base-provider.js'; +import MODEL_MAP from '../../scripts/modules/supported-models.json' with { type: 'json' }; export class AzureProvider extends BaseAIProvider { constructor() { @@ -37,21 +38,65 @@ export class AzureProvider extends BaseAIProvider { } } + /** + * Determines if a model requires the responses API endpoint instead of chat/completions + * @param {string} modelId - The model ID to check + * @returns {boolean} True if the model needs the responses API + */ + isReasoningModel(modelId) { + const azureModels = MODEL_MAP.azure || []; + const modelDef = azureModels.find(m => m.id === modelId); + return modelDef?.api_type === 'responses'; + } + + /** + * Adjusts the base URL for reasoning models that need the responses endpoint + * @param {string} baseURL - Original base URL + * @param {string} modelId - Model ID + * @returns {string} Adjusted base URL + */ + adjustBaseURL(baseURL, modelId) { + if (!this.isReasoningModel(modelId)) { + return baseURL; + } + + // Convert chat/completions URL to responses URL for reasoning models + if (baseURL.includes('/chat/completions')) { + return baseURL.replace('/chat/completions', '/responses'); + } + + // If baseURL ends with deployments/, add responses endpoint + if (baseURL.includes('/deployments/')) { + return baseURL.replace(/\/deployments\/[^\/]+$/, '/responses'); + } + + // If baseURL is just the base, ensure it ends with /responses + if (!baseURL.endsWith('/responses')) { + return baseURL.replace(/\/$/, '') + '/responses'; + } + + return baseURL; + } + /** * Creates and returns an Azure OpenAI client instance. * @param {object} params - Parameters for client initialization * @param {string} params.apiKey - Azure OpenAI API key * @param {string} params.baseURL - Azure OpenAI endpoint URL (from .taskmasterconfig global.azureBaseURL or models.[role].baseURL) + * @param {string} params.modelId - Model ID (used to determine API endpoint) * @returns {Function} Azure OpenAI client function * @throws {Error} If required parameters are missing or initialization fails */ getClient(params) { try { - const { apiKey, baseURL } = params; + const { apiKey, baseURL, modelId } = params; + + // Adjust base URL for reasoning models + const adjustedBaseURL = this.adjustBaseURL(baseURL, modelId); return createAzure({ apiKey, - baseURL + baseURL: adjustedBaseURL }); } catch (error) { this.handleError('client initialization', error); diff --git a/src/constants/providers.js b/src/constants/providers.js index d1bcf7ba..bb0ac271 100644 --- a/src/constants/providers.js +++ b/src/constants/providers.js @@ -11,7 +11,11 @@ export const VALIDATED_PROVIDERS = [ 'perplexity', 'xai', 'groq', - 'mistral' + 'mistral', + 'azure', + 'openrouter', + 'bedrock', + 'ollama' ]; // Custom providers object for easy named access