Skip to content

Mirror: Fix Azure/OpenAI endpoints, reject AI Inference URLs (#5860)#42

Closed
jeremylongshore wants to merge 1 commit intomainfrom
mirror/PR-5860
Closed

Mirror: Fix Azure/OpenAI endpoints, reject AI Inference URLs (#5860)#42
jeremylongshore wants to merge 1 commit intomainfrom
mirror/PR-5860

Conversation

@jeremylongshore
Copy link
Copy Markdown
Owner

Mirror of Kilo-Org#5860

Field Value
Upstream PR #5860
Author @Neonsy
Category feature
Tier 5
Size 327 lines, 5 files

This PR mirrors the upstream change for multi-AI review analysis.

Bot Review Tracker

  • CodeRabbit
  • Gemini Code Assist
  • Greptile
  • CodeQL
  • Qodo PR-Agent

Links

…odex and reject Azure AI Inference URLs

Normalize Responses base URLs for Azure/OpenAI fallback paths.
Use Azure auth/query behavior (api-key + api-version) for fallback requests.
Add openai-responses config validation and targeted tests.
@gemini-code-assist
Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 15, 2026

Warning

Rate limit exceeded

@jeremylongshore has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 20 minutes and 39 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch mirror/PR-5860

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review
Copy link
Copy Markdown

ⓘ You are approaching your monthly quota for Qodo. Upgrade your plan

Review Summary by Qodo

Fix Azure/OpenAI Responses endpoints and reject AI Inference URLs

🐞 Bug fix ✨ Enhancement

Grey Divider

Walkthroughs

Description
• Fix Azure/OpenAI Responses endpoint handling with proper URL normalization
• Reject unsupported Azure AI Inference URLs (*.services.ai.azure.com)
• Add configuration validation for openai-responses provider
• Implement fallback auth using api-key for Azure and Bearer token for standard endpoints
Diagram
flowchart LR
  A["Azure/OpenAI Endpoints"] --> B["URL Normalization"]
  B --> C["Remove api-version params"]
  B --> D["Normalize /v1 paths"]
  E["Azure AI Inference URLs"] --> F["Reject with Error"]
  G["Fallback Request Handler"] --> H["Use api-key for Azure"]
  G --> I["Use Bearer token for standard"]
  J["Config Validation"] --> K["Require baseUrl for responses"]
Loading

Grey Divider

File Changes

1. src/api/providers/openai-responses.ts 🐞 Bug fix +108/-29

Azure endpoint handling and fallback request normalization

• Add endpoint type detection for Azure AI Inference vs Azure OpenAI
• Implement assertSupportedResponsesEndpoint() to reject Azure AI Inference URLs
• Add getResponsesFallbackTarget() method to handle URL normalization and auth headers
• Implement normalizeResponsesBaseUrl() to properly normalize Azure and standard endpoints
• Remove unsupported path override for Azure Responses API calls
• Update error messages for 404 responses with Azure-specific guidance

src/api/providers/openai-responses.ts


2. src/api/providers/__tests__/openai-responses.spec.ts 🧪 Tests +153/-0

Comprehensive tests for endpoint normalization and validation

• Add test for URL normalization without duplicating /v1 path
• Add test rejecting Azure AI Inference endpoints with proper error
• Add test verifying no chat-completions path override for Azure Responses
• Add test for Azure OpenAI deployment URL normalization to /openai/v1/responses
• Add test for cognitiveservices Azure endpoint normalization

src/api/providers/tests/openai-responses.spec.ts


3. webview-ui/src/utils/validate.ts ✨ Enhancement +7/-0

Add openai-responses configuration validation

• Add validation case for openai-responses provider
• Require openAiBaseUrl, openAiApiKey, and openAiModelId for responses configuration

webview-ui/src/utils/validate.ts


View more (2)
4. webview-ui/src/utils/__tests__/validate.spec.ts 🧪 Tests +25/-0

Tests for openai-responses validation logic

• Add test for incomplete openai-responses configuration validation
• Add test for valid openai-responses configuration with Azure endpoint

webview-ui/src/utils/tests/validate.spec.ts


5. .changeset/azure-responses-v1-endpoints.md 📝 Documentation +5/-0

Changelog entry for Azure Responses endpoint fix

• Document fix for Azure v1 endpoint URL normalization
• Note removal of unsupported api-version parameters

.changeset/azure-responses-v1-endpoints.md


Grey Divider

Qodo Logo

@github-actions
Copy link
Copy Markdown

Failed to generate code suggestions for PR

@qodo-code-review
Copy link
Copy Markdown

ⓘ You are approaching your monthly quota for Qodo. Upgrade your plan

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (1) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Unmarked Responses tests additions 📘 Rule violation ⛯ Reliability
Description
New/modified tests in a shared upstream src/ test file were added without kilocode_change
markers even though they validate newly introduced Kilo-specific behavior. This can cause avoidable
conflicts during upstream syncs.
Code

src/api/providers/tests/openai-responses.spec.ts[R105-152]

+	it("normalizes fallback URL without duplicating /v1", async () => {
+		const handler = new OpenAiCompatibleResponsesHandler({
+			openAiApiKey: "test-key",
+			openAiBaseUrl: "https://api.example.com/v1",
+			openAiModelId: "gpt-4o",
+		} satisfies ApiHandlerOptions)
+
+		const mockFetch = vi.fn().mockResolvedValue({
+			ok: true,
+			body: new ReadableStream({
+				start(controller) {
+					controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n"))
+					controller.close()
+				},
+			}),
+		})
+		global.fetch = mockFetch as any
+		mockResponsesCreate.mockRejectedValue(new Error("SDK not available"))
+
+		const stream = handler.createMessage(systemPrompt, messages)
+		for await (const _chunk of stream) {
+		}
+
+		expect(mockFetch).toHaveBeenCalledWith(
+			"https://api.example.com/v1/responses",
+			expect.objectContaining({
+				method: "POST",
+			}),
+		)
+	})
+
+	it("rejects Azure AI Inference endpoints for Responses API", async () => {
+		const handler = new OpenAiCompatibleResponsesHandler({
+			openAiApiKey: "test-key",
+			openAiBaseUrl: "https://myresource.services.ai.azure.com/models",
+			openAiModelId: "gpt-5.2-codex",
+		} satisfies ApiHandlerOptions)
+
+		const stream = handler.createMessage(systemPrompt, messages)
+
+		await expect(async () => {
+			for await (const _chunk of stream) {
+			}
+		}).rejects.toThrow("Azure AI Inference endpoints")
+
+		await expect(handler.completePrompt("Test prompt")).rejects.toThrow("Azure AI Inference endpoints")
+	})
+
Evidence
The checklist requires Kilo-specific edits in shared upstream paths (including tests under src/)
to be marked with kilocode_change. The PR adds multiple new test cases in
src/api/providers/__tests__/openai-responses.spec.ts without any kilocode_change markers around
the new blocks.

AGENTS.md
src/api/providers/tests/openai-responses.spec.ts[105-152]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Kilo-specific edits in shared upstream files under `src/` must include `kilocode_change` markers. The PR adds new test cases for Responses/Azure endpoint normalization and rejection without markers.

## Issue Context
Other modified shared files in this PR already use `kilocode_change` markers, so leaving related tests unmarked is inconsistent and increases upstream sync friction.

## Fix Focus Areas
- src/api/providers/__tests__/openai-responses.spec.ts[105-152]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Silent baseUrl fallback 🐞 Bug ⛨ Security
Description
If openAiBaseUrl is present but not a valid absolute URL, normalizeResponsesBaseUrl() silently
returns a default host, so the fetch fallback may POST prompts to an unexpected endpoint instead of
failing fast on misconfiguration. This is particularly risky in environments relying on explicit
egress controls/allowed endpoints.
Code

src/api/providers/openai-responses.ts[R576-619]

+	private normalizeResponsesBaseUrl(baseUrl?: string): string {
+		const defaultBaseUrl = this.isAzureOpenAiEndpoint
+			? "https://api.openai.azure.com/openai/v1"
+			: "https://api.openai.com/v1"
+
+		if (!baseUrl) {
+			return defaultBaseUrl
+		}
+
+		try {
+			const parsed = new URL(baseUrl)
+			parsed.search = ""
+			parsed.hash = ""
+
+			let pathname = parsed.pathname.replace(/\/+$/, "")
+			pathname = pathname.replace(/\/(chat\/completions|completions|responses)$/, "")
+
+			if (this.isAzureOpenAiEndpoint) {
+				if (/\/openai\/deployments\/[^/]+$/.test(pathname)) {
+					pathname = "/openai/v1"
+				} else if (pathname === "" || pathname === "/") {
+					pathname = "/openai/v1"
+				} else if (pathname === "/v1") {
+					pathname = "/openai/v1"
+				} else if (pathname === "/openai") {
+					pathname = "/openai/v1"
+				} else if (pathname.endsWith("/openai")) {
+					pathname = `${pathname}/v1`
+				} else if (!pathname.endsWith("/v1")) {
+					pathname = `${pathname}/v1`
+				}
+			} else {
+				if (pathname === "" || pathname === "/") {
+					pathname = "/v1"
+				} else if (!pathname.endsWith("/v1")) {
+					pathname = `${pathname}/v1`
+				}
+			}
+
+			parsed.pathname = pathname
+			return parsed.toString().replace(/\/$/, "")
+		} catch {
+			return defaultBaseUrl
+		}
Evidence
The fallback request target is built from normalizeResponsesBaseUrl(); on URL parse failure it
returns a default base URL instead of erroring, so a malformed configured base URL can be ignored
and replaced with a hardcoded default when the SDK path falls back to fetch.

src/api/providers/openai-responses.ts[557-574]
src/api/providers/openai-responses.ts[576-619]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`normalizeResponsesBaseUrl()` silently falls back to a hardcoded default host when `openAiBaseUrl` cannot be parsed as a URL. This can cause the fetch fallback path to send requests to an unintended endpoint instead of surfacing a clear configuration error.

### Issue Context
The fallback target is created by `getResponsesFallbackTarget()`, which always uses `normalizeResponsesBaseUrl()` to construct the `fetch()` URL.

### Fix Focus Areas
- src/api/providers/openai-responses.ts[557-574]
- src/api/providers/openai-responses.ts[576-619]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. UI baseUrl requirement mismatch 🐞 Bug ✓ Correctness
Description
The webview validation now requires openAiBaseUrl for the openai-responses provider, while the
backend handler still has a default base URL. Since the settings UI does not prefill openAiBaseUrl
(it renders empty when undefined), this can block users/migrations unless some other layer populates
it.
Code

webview-ui/src/utils/validate.ts[R108-112]

+		case "openai-responses":
+			if (!apiConfiguration.openAiBaseUrl || !apiConfiguration.openAiApiKey || !apiConfiguration.openAiModelId) {
+				return i18next.t("settings:validation.openAi")
+			}
+			break
Evidence
UI validation rejects openai-responses configs missing openAiBaseUrl, but the backend handler
defaults to https://api.openai.com/v1 when openAiBaseUrl is undefined. The OpenAICompatible settings
component also displays an empty base URL when not set, implying no automatic defaulting in the UI
layer.

webview-ui/src/utils/validate.ts[102-113]
src/api/providers/openai-responses.ts[43-44]
webview-ui/src/components/settings/providers/OpenAICompatible.tsx[142-149]
src/api/providers/tests/openai-responses.spec.ts[50-56]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The UI validation requires `openAiBaseUrl` for `openai-responses`, but the settings UI doesn’t appear to prefill it and the backend can operate with a default. This can block users with otherwise workable configs or older saved profiles.

### Issue Context
`OpenAICompatible` renders an empty string when `openAiBaseUrl` is undefined, so users will hit the new validation unless another layer sets a default.

### Fix Focus Areas
- webview-ui/src/utils/validate.ts[102-113]
- webview-ui/src/components/settings/ApiOptions.tsx[374-422]
- webview-ui/src/components/settings/providers/OpenAICompatible.tsx[140-149]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +105 to +152
it("normalizes fallback URL without duplicating /v1", async () => {
const handler = new OpenAiCompatibleResponsesHandler({
openAiApiKey: "test-key",
openAiBaseUrl: "https://api.example.com/v1",
openAiModelId: "gpt-4o",
} satisfies ApiHandlerOptions)

const mockFetch = vi.fn().mockResolvedValue({
ok: true,
body: new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n"))
controller.close()
},
}),
})
global.fetch = mockFetch as any
mockResponsesCreate.mockRejectedValue(new Error("SDK not available"))

const stream = handler.createMessage(systemPrompt, messages)
for await (const _chunk of stream) {
}

expect(mockFetch).toHaveBeenCalledWith(
"https://api.example.com/v1/responses",
expect.objectContaining({
method: "POST",
}),
)
})

it("rejects Azure AI Inference endpoints for Responses API", async () => {
const handler = new OpenAiCompatibleResponsesHandler({
openAiApiKey: "test-key",
openAiBaseUrl: "https://myresource.services.ai.azure.com/models",
openAiModelId: "gpt-5.2-codex",
} satisfies ApiHandlerOptions)

const stream = handler.createMessage(systemPrompt, messages)

await expect(async () => {
for await (const _chunk of stream) {
}
}).rejects.toThrow("Azure AI Inference endpoints")

await expect(handler.completePrompt("Test prompt")).rejects.toThrow("Azure AI Inference endpoints")
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Unmarked responses tests additions 📘 Rule violation ⛯ Reliability

New/modified tests in a shared upstream src/ test file were added without kilocode_change
markers even though they validate newly introduced Kilo-specific behavior. This can cause avoidable
conflicts during upstream syncs.
Agent Prompt
## Issue description
Kilo-specific edits in shared upstream files under `src/` must include `kilocode_change` markers. The PR adds new test cases for Responses/Azure endpoint normalization and rejection without markers.

## Issue Context
Other modified shared files in this PR already use `kilocode_change` markers, so leaving related tests unmarked is inconsistent and increases upstream sync friction.

## Fix Focus Areas
- src/api/providers/__tests__/openai-responses.spec.ts[105-152]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +576 to +619
private normalizeResponsesBaseUrl(baseUrl?: string): string {
const defaultBaseUrl = this.isAzureOpenAiEndpoint
? "https://api.openai.azure.com/openai/v1"
: "https://api.openai.com/v1"

if (!baseUrl) {
return defaultBaseUrl
}

try {
const parsed = new URL(baseUrl)
parsed.search = ""
parsed.hash = ""

let pathname = parsed.pathname.replace(/\/+$/, "")
pathname = pathname.replace(/\/(chat\/completions|completions|responses)$/, "")

if (this.isAzureOpenAiEndpoint) {
if (/\/openai\/deployments\/[^/]+$/.test(pathname)) {
pathname = "/openai/v1"
} else if (pathname === "" || pathname === "/") {
pathname = "/openai/v1"
} else if (pathname === "/v1") {
pathname = "/openai/v1"
} else if (pathname === "/openai") {
pathname = "/openai/v1"
} else if (pathname.endsWith("/openai")) {
pathname = `${pathname}/v1`
} else if (!pathname.endsWith("/v1")) {
pathname = `${pathname}/v1`
}
} else {
if (pathname === "" || pathname === "/") {
pathname = "/v1"
} else if (!pathname.endsWith("/v1")) {
pathname = `${pathname}/v1`
}
}

parsed.pathname = pathname
return parsed.toString().replace(/\/$/, "")
} catch {
return defaultBaseUrl
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Silent baseurl fallback 🐞 Bug ⛨ Security

If openAiBaseUrl is present but not a valid absolute URL, normalizeResponsesBaseUrl() silently
returns a default host, so the fetch fallback may POST prompts to an unexpected endpoint instead of
failing fast on misconfiguration. This is particularly risky in environments relying on explicit
egress controls/allowed endpoints.
Agent Prompt
### Issue description
`normalizeResponsesBaseUrl()` silently falls back to a hardcoded default host when `openAiBaseUrl` cannot be parsed as a URL. This can cause the fetch fallback path to send requests to an unintended endpoint instead of surfacing a clear configuration error.

### Issue Context
The fallback target is created by `getResponsesFallbackTarget()`, which always uses `normalizeResponsesBaseUrl()` to construct the `fetch()` URL.

### Fix Focus Areas
- src/api/providers/openai-responses.ts[557-574]
- src/api/providers/openai-responses.ts[576-619]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@jeremylongshore
Copy link
Copy Markdown
Owner Author

@greptileai review

@jeremylongshore
Copy link
Copy Markdown
Owner Author

@greptileai

@jeremylongshore
Copy link
Copy Markdown
Owner Author

Merged into batch-6-combined-20-mirrors branch for combined testing. Bot reviews collected. Review artifacts preserved in .reviews/.

@jeremylongshore
Copy link
Copy Markdown
Owner Author

Review: kilocode Kilo-Org#5860

Fix Azure/OpenAI endpoints and reject Azure AI Inference URLs by @Neonsy

Methodology

Checklist

Check Result Notes
Correctness PASS Fixes Azure deployment URL normalization; rejects unsupported Azure AI Inference endpoints
Conventions PASS Uses kilocode_change markers, adds validation for openai-responses provider
Changeset PASS Patch changeset included
Tests PASS 153 new test lines covering URL normalization, Azure auth, endpoint rejection, validation
i18n N/A Error messages are technical/descriptive, not user-facing i18n
Types PASS No type changes needed
Security PASS URL validation prevents misrouting; auth header separation (api-key vs Bearer) is correct
Scope PASS Focused on openai-responses provider Azure endpoint handling

Findings

1. Azure AI Inference endpoint rejection is a good guard (severity: gray)

The PR adds an explicit check that rejects *.services.ai.azure.com endpoints with a clear error message:

private assertSupportedResponsesEndpoint(): void {
    if (this.isAzureAiInferenceEndpoint) {
        throw new Error(
            "Azure AI Inference endpoints (*.services.ai.azure.com) are not supported by OpenAI Compatible (Responses)..."
        )
    }
}

This is called at three entry points: createMessage, makeResponsesApiRequest, and completePrompt. The error message includes guidance to use Azure OpenAI instead. This is better than silently failing with confusing 404s.

2. URL normalization handles many edge cases (severity: gray)

The normalizeResponsesBaseUrl method handles:

  • Stripping /chat/completions, /completions, /responses suffixes
  • Converting /openai/deployments/<name> to /openai/v1
  • Azure endpoints without /openai path get /openai/v1 appended
  • Bare /v1 on Azure becomes /openai/v1
  • Non-Azure bare paths get /v1 appended
  • Query parameters and hashes are stripped

This is necessarily complex because Azure users provide URLs in many different formats. The test coverage validates the important cases.

3. api-version parameter correctly omitted for /openai/v1 paths (severity: gray)

private shouldAppendAzureApiVersion(url: URL): boolean {
    const pathname = url.pathname.replace(/\/+$/, "")
    return !pathname.includes("/openai/v1")
}

Azure's /openai/v1 path does not require an api-version query parameter. This is correct per Azure OpenAI documentation -- the v1 API endpoint is version-agnostic.

4. Removed Azure AI Inference client construction from constructor (severity: yellow)

The original constructor had three branches: Azure AI Inference, Azure OpenAI, and standard OpenAI. The PR removes the Azure AI Inference branch entirely and converts it to a runtime rejection. The isAzureAiInferenceEndpoint flag is now only used for the assertSupportedResponsesEndpoint guard.

This means the OPENAI_AZURE_AI_INFERENCE_PATH import is also removed. This is a behavioral change -- previously, Azure AI Inference endpoints would get a configured client that might partially work; now they fail immediately with a clear error. This is the correct approach since the Responses API is not supported on Azure AI Inference.

5. Fallback auth correctly separates Azure vs non-Azure (severity: gray)

if (this.isAzureOpenAiEndpoint) {
    headers["api-key"] = apiKey
    // Conditionally add api-version
} else {
    headers.Authorization = `Bearer ${apiKey}`
}

Azure OpenAI uses api-key header, while standard OpenAI uses Authorization: Bearer. The previous code was sending Authorization for all endpoints in the fallback path, which would fail for Azure. Tests validate both paths.

6. webview validation added for openai-responses (severity: gray)

case "openai-responses":
    if (!apiConfiguration.openAiBaseUrl || !apiConfiguration.openAiApiKey || !apiConfiguration.openAiModelId) {
        return i18next.t("settings:validation.openAi")
    }
    break

This is a good addition -- openai-responses was missing from the validation switch. All three fields (base URL, API key, model ID) are correctly required.

7. 404 error message improvement for Azure users (severity: gray)

The 404 error message now includes Azure-specific guidance:

errorMessage = this.isAzureOpenAiEndpoint
    ? "...use a base URL like https://<resource>.openai.azure.com/openai/v1..."
    : "...The endpoint may not be available yet..."
if ((this.options.openAiBaseUrl || "").includes("/deployments/")) {
    errorMessage += " Do not use a /deployments/.../chat/completions URL as the base URL."
}

This directly addresses a common misconfiguration pattern.

CI Status

Check Result
compile PASS
test-extension (ubuntu) PASS
test-extension (windows) PASS
test-webview (ubuntu) PASS
test-webview (windows) PASS
test-cli PASS
test-jetbrains PASS
build-cli PASS
check-translations PASS

Code Snippets

URL normalization for Azure deployment paths:

if (/\/openai\/deployments\/[^/]+$/.test(pathname)) {
    pathname = "/openai/v1"
} else if (pathname === "" || pathname === "/") {
    pathname = "/openai/v1"
} else if (pathname === "/v1") {
    pathname = "/openai/v1"
} else if (pathname === "/openai") {
    pathname = "/openai/v1"
}

Test validates correct normalization for complex Azure URL:

// Input: https://myresource.openai.azure.com/openai/deployments/my-deployment/chat/completions?api-version=2024-05-01-preview
// Output: https://myresource.openai.azure.com/openai/v1/responses (no api-version)

Verdict

APPROVE -- This PR correctly fixes Azure URL handling in the OpenAI Responses provider. The URL normalization handles the many variations of Azure endpoint formats that users provide. The Azure AI Inference rejection fails fast with a clear message instead of silently sending requests to an unsupported endpoint. Auth header separation is correct. All CI checks pass and a Discord user confirmed the fix works with Microsoft Foundry deployments. The test coverage is targeted and validates the critical paths.

@jeremylongshore
Copy link
Copy Markdown
Owner Author

Review Journal: kilocode Kilo-Org#5860

PR: #5860 |
Title: Fix Azure/OpenAI endpoints and reject Azure AI Inference URLs |
Author: @Neonsy |
Category: feature | Tier: 5 | Size: 327 lines, 5 files


Summary

Fixes Azure URL normalization in the OpenAI Responses provider and rejects unsupported Azure AI Inference endpoints. Handles the many URL format variations users provide for Azure deployments. Community user confirmed fix works with Microsoft Foundry. Approve.

First Impressions

Azure endpoint handling is notoriously complex due to the multiple URL formats (deployment paths, cognitive services, v1 paths, etc.). The PR description clearly explains the endpoint classification and normalization approach. The test coverage at 153 lines covers the critical URL transformations.

What I Looked At

  • src/api/providers/openai-responses.ts -- core handler changes (108 additions, 29 deletions)
  • src/api/providers/__tests__/openai-responses.spec.ts -- new tests (153 lines)
  • webview-ui/src/utils/validate.ts -- validation addition for openai-responses
  • webview-ui/src/utils/__tests__/validate.spec.ts -- validation tests
  • Cross-referenced the existing constructor on main (Azure AI Inference branch, Azure OpenAI branch, standard branch)
  • Verified OPENAI_AZURE_AI_INFERENCE_PATH usage before/after

Analysis

The PR makes three categories of changes:

1. Endpoint classification and rejection

Two boolean flags are set in the constructor:

  • isAzureAiInferenceEndpoint: *.services.ai.azure.com -- rejected at runtime
  • isAzureOpenAiEndpoint: any *.azure.com host or explicit openAiUseAzure flag

The Azure AI Inference path was previously handled by creating a client with a specific path override. The PR removes this in favor of an immediate rejection since the Responses API is not supported on these endpoints. This is the correct approach -- failing fast with a clear message is better than partially working.

2. URL normalization

The normalizeResponsesBaseUrl method is the most complex addition. It handles:

  • Stripping endpoint suffixes (/chat/completions, /completions, /responses)
  • Converting deployment paths (/openai/deployments/<name>) to /openai/v1
  • Ensuring Azure endpoints always have /openai/v1
  • Ensuring non-Azure endpoints always have /v1
  • Stripping query parameters (especially api-version which should only be added when needed)

The shouldAppendAzureApiVersion check correctly exempts /openai/v1 paths.

3. Auth header separation

The fallback fetch path now correctly uses api-key header for Azure and Authorization: Bearer for non-Azure. Previously, all fallback requests used Bearer auth, which would fail for Azure deployments.

Verification

  • All CI checks pass
  • Discord user @Anthonix24812 confirmed fix works with Microsoft Foundry deployment of gpt-5.2-codex
  • Tests cover: URL normalization, Azure auth headers, AI Inference rejection, deployment URL rewriting, cognitive services endpoints, webview validation

Lessons Learned

  • Azure endpoint URLs come in many flavors -- deployment paths, v1 paths, cognitive services, AI Inference -- and each requires different handling
  • Failing fast for unsupported endpoint types is better than attempting partial compatibility
  • Auth header separation (api-key vs Bearer) is a common source of Azure integration issues
  • The Responses API being unavailable on Azure AI Inference is a platform limitation that should be documented clearly

Review methodology: AI PR Review Case Studies | Reviewed with GWI + Claude Code

@jeremylongshore jeremylongshore deleted the mirror/PR-5860 branch February 16, 2026 00:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants