From e108f4310c28f05a5da6bd5c0bf51798503a31fd Mon Sep 17 00:00:00 2001 From: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com> Date: Fri, 7 Nov 2025 22:24:30 +0100 Subject: [PATCH] Merge pull request #1388 from eyaltoledano/ralph/chore/apply.requested.changes chore: apply requested coderabbit changes --- src/ai-providers/anthropic.js | 9 +++------ src/ai-providers/azure.js | 5 +++-- src/ai-providers/bedrock.js | 3 ++- src/ai-providers/google-vertex.js | 6 ++---- src/ai-providers/google.js | 9 +++------ src/ai-providers/groq.js | 4 ---- src/ai-providers/openai-compatible.js | 15 +++++++-------- src/ai-providers/openai.js | 9 +++------ src/ai-providers/openrouter.js | 4 ---- src/ai-providers/perplexity.js | 9 +++------ src/ai-providers/xai.js | 4 ---- tests/unit/ai-providers/openai-compatible.test.js | 8 ++++---- tests/unit/ai-providers/openai.test.js | 6 ++++-- tests/unit/ai-providers/zai-coding.test.js | 8 ++++---- tests/unit/ai-providers/zai.test.js | 8 ++++---- tests/unit/config-manager.test.js | 4 ++-- 16 files changed, 44 insertions(+), 67 deletions(-) diff --git a/src/ai-providers/anthropic.js b/src/ai-providers/anthropic.js index 1791ac65..e8e01934 100644 --- a/src/ai-providers/anthropic.js +++ b/src/ai-providers/anthropic.js @@ -37,15 +37,12 @@ export class AnthropicAIProvider extends BaseAIProvider { * @param {string} params.apiKey - Anthropic API key * @param {string} [params.baseURL] - Optional custom API endpoint * @returns {Function} Anthropic client function - * @throws {Error} If API key is missing or initialization fails + * @throws {Error} If initialization fails */ getClient(params) { try { const { apiKey, baseURL } = params; - - if (!apiKey) { - throw new Error('Anthropic API key is required.'); - } + const fetchImpl = this.createProxyFetch(); return createAnthropic({ apiKey, @@ -53,7 +50,7 @@ export class AnthropicAIProvider extends BaseAIProvider { headers: { 'anthropic-beta': 'output-128k-2025-02-19' }, - fetch: this.createProxyFetch() + ...(fetchImpl && { fetch: fetchImpl }) }); } catch (error) { this.handleError('client initialization', error); diff --git a/src/ai-providers/azure.js b/src/ai-providers/azure.js index 4d3ea62e..c6dcb1cb 100644 --- a/src/ai-providers/azure.js +++ b/src/ai-providers/azure.js @@ -43,16 +43,17 @@ export class AzureProvider extends BaseAIProvider { * @param {string} params.apiKey - Azure OpenAI API key * @param {string} params.baseURL - Azure OpenAI endpoint URL (from .taskmasterconfig global.azureBaseURL or models.[role].baseURL) * @returns {Function} Azure OpenAI client function - * @throws {Error} If required parameters are missing or initialization fails + * @throws {Error} If client initialization fails */ getClient(params) { try { const { apiKey, baseURL } = params; + const fetchImpl = this.createProxyFetch(); return createAzure({ apiKey, baseURL, - fetch: this.createProxyFetch() + ...(fetchImpl && { fetch: fetchImpl }) }); } catch (error) { this.handleError('client initialization', error); diff --git a/src/ai-providers/bedrock.js b/src/ai-providers/bedrock.js index 6c212410..1203b217 100644 --- a/src/ai-providers/bedrock.js +++ b/src/ai-providers/bedrock.js @@ -35,10 +35,11 @@ export class BedrockAIProvider extends BaseAIProvider { getClient(params) { try { const credentialProvider = fromNodeProviderChain(); + const fetchImpl = this.createProxyFetch(); return createAmazonBedrock({ credentialProvider, - fetch: this.createProxyFetch() + ...(fetchImpl && { fetch: fetchImpl }) }); } catch (error) { this.handleError('client initialization', error); diff --git a/src/ai-providers/google-vertex.js b/src/ai-providers/google-vertex.js index 29469548..3a8f4fe9 100644 --- a/src/ai-providers/google-vertex.js +++ b/src/ai-providers/google-vertex.js @@ -91,10 +91,8 @@ export class VertexAIProvider extends BaseAIProvider { */ getClient(params) { try { - // Validate required parameters - this.validateAuth(params); - const { apiKey, projectId, location, credentials, baseURL } = params; + const fetchImpl = this.createProxyFetch(); // Configure auth options - either API key or service account const authOptions = {}; @@ -110,7 +108,7 @@ export class VertexAIProvider extends BaseAIProvider { projectId, location, ...(baseURL && { baseURL }), - fetch: this.createProxyFetch() + ...(fetchImpl && { fetch: fetchImpl }) }); } catch (error) { this.handleError('client initialization', error); diff --git a/src/ai-providers/google.js b/src/ai-providers/google.js index d0349d44..f27c160e 100644 --- a/src/ai-providers/google.js +++ b/src/ai-providers/google.js @@ -26,20 +26,17 @@ export class GoogleAIProvider extends BaseAIProvider { * @param {string} params.apiKey - Google API key * @param {string} [params.baseURL] - Optional custom API endpoint * @returns {Function} Google AI client function - * @throws {Error} If API key is missing or initialization fails + * @throws {Error} If initialization fails */ getClient(params) { try { const { apiKey, baseURL } = params; - - if (!apiKey) { - throw new Error('Google API key is required.'); - } + const fetchImpl = this.createProxyFetch(); return createGoogleGenerativeAI({ apiKey, ...(baseURL && { baseURL }), - fetch: this.createProxyFetch() + ...(fetchImpl && { fetch: fetchImpl }) }); } catch (error) { this.handleError('client initialization', error); diff --git a/src/ai-providers/groq.js b/src/ai-providers/groq.js index 8acbd6df..e13d9b5a 100644 --- a/src/ai-providers/groq.js +++ b/src/ai-providers/groq.js @@ -34,10 +34,6 @@ export class GroqProvider extends BaseAIProvider { try { const { apiKey, baseURL } = params; - if (!apiKey) { - throw new Error('Groq API key is required.'); - } - return createGroq({ apiKey, ...(baseURL && { baseURL }) diff --git a/src/ai-providers/openai-compatible.js b/src/ai-providers/openai-compatible.js index 3621dccd..35974a23 100644 --- a/src/ai-providers/openai-compatible.js +++ b/src/ai-providers/openai-compatible.js @@ -96,19 +96,13 @@ export class OpenAICompatibleProvider extends BaseAIProvider { getClient(params) { try { const { apiKey } = params; - - // Validate API key if required - if (this.requiresApiKey && !apiKey) { - throw new Error(`${this.name} API key is required.`); - } + const fetchImpl = this.createProxyFetch(); const baseURL = this.getBaseURL(params); const clientConfig = { // Provider name for SDK (required, used for logging/debugging) - name: this.name.toLowerCase().replace(/[^a-z0-9]/g, '-'), - // Add proxy support - fetch: this.createProxyFetch() + name: this.name.toLowerCase().replace(/[^a-z0-9]/g, '-') }; // Only include apiKey if provider requires it @@ -126,6 +120,11 @@ export class OpenAICompatibleProvider extends BaseAIProvider { clientConfig.supportsStructuredOutputs = this.supportsStructuredOutputs; } + // Add proxy support if available + if (fetchImpl) { + clientConfig.fetch = fetchImpl; + } + return createOpenAICompatible(clientConfig); } catch (error) { this.handleError('client initialization', error); diff --git a/src/ai-providers/openai.js b/src/ai-providers/openai.js index 755b62ec..ff104d85 100644 --- a/src/ai-providers/openai.js +++ b/src/ai-providers/openai.js @@ -26,20 +26,17 @@ export class OpenAIProvider extends BaseAIProvider { * @param {string} params.apiKey - OpenAI API key * @param {string} [params.baseURL] - Optional custom API endpoint * @returns {Function} OpenAI client function - * @throws {Error} If API key is missing or initialization fails + * @throws {Error} If initialization fails */ getClient(params) { try { const { apiKey, baseURL } = params; - - if (!apiKey) { - throw new Error('OpenAI API key is required.'); - } + const fetchImpl = this.createProxyFetch(); return createOpenAI({ apiKey, ...(baseURL && { baseURL }), - fetch: this.createProxyFetch() + ...(fetchImpl && { fetch: fetchImpl }) }); } catch (error) { this.handleError('client initialization', error); diff --git a/src/ai-providers/openrouter.js b/src/ai-providers/openrouter.js index f8ca56a1..4326c99c 100644 --- a/src/ai-providers/openrouter.js +++ b/src/ai-providers/openrouter.js @@ -32,10 +32,6 @@ export class OpenRouterAIProvider extends BaseAIProvider { try { const { apiKey, baseURL } = params; - if (!apiKey) { - throw new Error('OpenRouter API key is required.'); - } - return createOpenRouter({ apiKey, ...(baseURL && { baseURL }) diff --git a/src/ai-providers/perplexity.js b/src/ai-providers/perplexity.js index 87df794a..152cb2b2 100644 --- a/src/ai-providers/perplexity.js +++ b/src/ai-providers/perplexity.js @@ -26,20 +26,17 @@ export class PerplexityAIProvider extends BaseAIProvider { * @param {string} params.apiKey - Perplexity API key * @param {string} [params.baseURL] - Optional custom API endpoint * @returns {Function} Perplexity client function - * @throws {Error} If API key is missing or initialization fails + * @throws {Error} If initialization fails */ getClient(params) { try { const { apiKey, baseURL } = params; - - if (!apiKey) { - throw new Error('Perplexity API key is required.'); - } + const fetchImpl = this.createProxyFetch(); return createPerplexity({ apiKey, baseURL: baseURL || 'https://api.perplexity.ai', - fetch: this.createProxyFetch() + ...(fetchImpl && { fetch: fetchImpl }) }); } catch (error) { this.handleError('client initialization', error); diff --git a/src/ai-providers/xai.js b/src/ai-providers/xai.js index d367d513..6a24ecd5 100644 --- a/src/ai-providers/xai.js +++ b/src/ai-providers/xai.js @@ -32,10 +32,6 @@ export class XAIProvider extends BaseAIProvider { try { const { apiKey, baseURL } = params; - if (!apiKey) { - throw new Error('xAI API key is required.'); - } - return createXai({ apiKey, baseURL: baseURL || 'https://api.x.ai/v1' diff --git a/tests/unit/ai-providers/openai-compatible.test.js b/tests/unit/ai-providers/openai-compatible.test.js index 191e28b5..7d2d0725 100644 --- a/tests/unit/ai-providers/openai-compatible.test.js +++ b/tests/unit/ai-providers/openai-compatible.test.js @@ -175,16 +175,16 @@ describe('OpenAICompatibleProvider', () => { expect(client).toBeDefined(); }); - it('should throw error when API key is required but missing', () => { + it('should create client even when API key is required but missing (validation deferred to SDK)', () => { const provider = new OpenAICompatibleProvider({ name: 'Test Provider', apiKeyEnvVar: 'TEST_API_KEY', requiresApiKey: true }); - expect(() => { - provider.getClient({}); - }).toThrow('Test Provider API key is required.'); + // getClient() no longer validates API key - validation is deferred to SDK initialization + const client = provider.getClient({}); + expect(typeof client).toBe('function'); }); }); }); diff --git a/tests/unit/ai-providers/openai.test.js b/tests/unit/ai-providers/openai.test.js index 5f2ee6b8..c24e833e 100644 --- a/tests/unit/ai-providers/openai.test.js +++ b/tests/unit/ai-providers/openai.test.js @@ -85,8 +85,10 @@ describe('OpenAIProvider', () => { }); describe('getClient', () => { - it('should throw error if API key is missing', () => { - expect(() => provider.getClient({})).toThrow(Error); + it('should create client even without API key (validation deferred to SDK)', () => { + // getClient() no longer validates API key - validation is deferred to SDK initialization + const client = provider.getClient({}); + expect(typeof client).toBe('function'); }); it('should create client with apiKey only', () => { diff --git a/tests/unit/ai-providers/zai-coding.test.js b/tests/unit/ai-providers/zai-coding.test.js index 008a3c2c..a15f7459 100644 --- a/tests/unit/ai-providers/zai-coding.test.js +++ b/tests/unit/ai-providers/zai-coding.test.js @@ -57,10 +57,10 @@ describe('ZAICodingProvider', () => { // The provider should use the coding endpoint }); - it('should throw error when API key is missing', () => { - expect(() => { - provider.getClient({}); - }).toThrow('Z.ai (Coding Plan) API key is required.'); + it('should create client even without API key (validation deferred to SDK)', () => { + // getClient() no longer validates API key - validation is deferred to SDK initialization + const client = provider.getClient({}); + expect(typeof client).toBe('function'); }); }); diff --git a/tests/unit/ai-providers/zai.test.js b/tests/unit/ai-providers/zai.test.js index b16a1c39..970261fb 100644 --- a/tests/unit/ai-providers/zai.test.js +++ b/tests/unit/ai-providers/zai.test.js @@ -55,10 +55,10 @@ describe('ZAIProvider', () => { expect(client).toBeDefined(); }); - it('should throw error when API key is missing', () => { - expect(() => { - provider.getClient({}); - }).toThrow('Z.ai API key is required.'); + it('should create client even without API key (validation deferred to SDK)', () => { + // getClient() no longer validates API key - validation is deferred to SDK initialization + const client = provider.getClient({}); + expect(typeof client).toBe('function'); }); }); diff --git a/tests/unit/config-manager.test.js b/tests/unit/config-manager.test.js index 074a668a..134a337c 100644 --- a/tests/unit/config-manager.test.js +++ b/tests/unit/config-manager.test.js @@ -70,13 +70,13 @@ const realSupportedModelsPath = path.resolve( '../../scripts/modules/supported-models.json' ); let REAL_SUPPORTED_MODELS_CONTENT; -let _REAL_SUPPORTED_MODELS_DATA; +let REAL_SUPPORTED_MODELS_DATA; try { REAL_SUPPORTED_MODELS_CONTENT = fs.readFileSync( realSupportedModelsPath, 'utf-8' ); - _REAL_SUPPORTED_MODELS_DATA = JSON.parse(REAL_SUPPORTED_MODELS_CONTENT); + REAL_SUPPORTED_MODELS_DATA = JSON.parse(REAL_SUPPORTED_MODELS_CONTENT); } catch (err) { console.error( 'FATAL TEST SETUP ERROR: Could not read or parse real supported-models.json',