From 4c9b9f0d5da44ad182baf5035a8b45ba533db452 Mon Sep 17 00:00:00 2001 From: gnanam1990 Date: Thu, 2 Apr 2026 01:32:30 +0530 Subject: [PATCH 1/2] fix: support Azure Cognitive Services and Azure OpenAI endpoints Azure endpoints require two changes vs standard OpenAI: 1. Auth header: `api-key: {key}` instead of `Authorization: Bearer {key}` 2. URL path: `/openai/deployments/{model}/chat/completions?api-version={version}` instead of `/chat/completions` Detection is automatic when OPENAI_BASE_URL contains `cognitiveservices.azure.com` or `openai.azure.com`. The api-version defaults to `2024-12-01-preview` and can be overridden via the AZURE_OPENAI_API_VERSION env var. Handles all common Azure base URL formats: - https://{resource}.cognitiveservices.azure.com/ - https://{resource}.cognitiveservices.azure.com/openai/v1 - https://{resource}.openai.azure.com/openai/v1 - https://{resource}.cognitiveservices.azure.com/openai/deployments/{model}/v1 Fixes #79 Co-Authored-By: Claude Sonnet 4.6 --- src/services/api/openaiShim.ts | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/services/api/openaiShim.ts b/src/services/api/openaiShim.ts index 4b70a63c..40b5cfaa 100644 --- a/src/services/api/openaiShim.ts +++ b/src/services/api/openaiShim.ts @@ -682,11 +682,40 @@ class OpenAIShimMessages { } const apiKey = process.env.OPENAI_API_KEY ?? '' + const isAzure = /cognitiveservices\.azure\.com|openai\.azure\.com/.test(request.baseUrl) + if (apiKey) { - headers.Authorization = `Bearer ${apiKey}` + if (isAzure) { + // Azure uses api-key header instead of Bearer token + headers['api-key'] = apiKey + } else { + headers.Authorization = `Bearer ${apiKey}` + } } - const response = await fetch(`${request.baseUrl}/chat/completions`, { + // Build the chat completions URL + // Azure Cognitive Services / Azure OpenAI require a deployment-specific path + // and an api-version query parameter. + // Standard format: {base}/openai/deployments/{model}/chat/completions?api-version={version} + // Non-Azure: {base}/chat/completions + let chatCompletionsUrl: string + if (isAzure) { + const apiVersion = process.env.AZURE_OPENAI_API_VERSION ?? '2024-12-01-preview' + const deployment = request.resolvedModel ?? process.env.OPENAI_MODEL ?? 'gpt-4o' + // If base URL already contains /deployments/, use it as-is with api-version + if (/\/deployments\//i.test(request.baseUrl)) { + const base = request.baseUrl.replace(/\/+$/, '') + chatCompletionsUrl = `${base}/chat/completions?api-version=${apiVersion}` + } else { + // Strip trailing /v1 or /openai/v1 if present, then build Azure path + const base = request.baseUrl.replace(/\/(openai\/)?v1\/?$/, '').replace(/\/+$/, '') + chatCompletionsUrl = `${base}/openai/deployments/${deployment}/chat/completions?api-version=${apiVersion}` + } + } else { + chatCompletionsUrl = `${request.baseUrl}/chat/completions` + } + + const response = await fetch(chatCompletionsUrl, { method: 'POST', headers, body: JSON.stringify(body), From ac2ea6aeb2e8af20f2905c3f85fa5289f498dead Mon Sep 17 00:00:00 2001 From: gnanam1990 Date: Thu, 2 Apr 2026 08:16:51 +0530 Subject: [PATCH 2/2] test: align codexShim test with strict schema normalization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the stale test expectation to match current behavior where normalizeSchemaForOpenAI() promotes all properties into required[] and marks the schema as strict: true. Same fix as PR #72 — included here so PR #80 passes CI independently. Co-Authored-By: Claude Sonnet 4.6 --- src/services/api/codexShim.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/services/api/codexShim.test.ts b/src/services/api/codexShim.test.ts index 6597c0f7..bee8f5ca 100644 --- a/src/services/api/codexShim.test.ts +++ b/src/services/api/codexShim.test.ts @@ -72,7 +72,7 @@ describe('Codex provider config', () => { }) describe('Codex request translation', () => { - test('disables strict mode for tools with optional parameters', () => { + test('normalizes optional parameters into strict Responses schemas', () => { const tools = convertToolsToResponsesTools([ { name: 'Agent', @@ -102,9 +102,10 @@ describe('Codex request translation', () => { prompt: { type: 'string' }, subagent_type: { type: 'string' }, }, - required: ['description', 'prompt'], + required: ['description', 'prompt', 'subagent_type'], additionalProperties: false, }, + strict: true, }, ]) })