mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-18 13:45:23 +02:00
Refactor: Extract Azure URL normalization to shared utility function
Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { normalizeAzureUrl } from "@dokploy/server/utils/ai/select-ai-provider";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
/**
|
||||
@@ -8,60 +9,42 @@ import { describe, expect, it } from "vitest";
|
||||
describe("Azure OpenAI URL Normalization", () => {
|
||||
it("should strip /openai/v1 from Azure URL", () => {
|
||||
const input = "https://workspacename.openai.azure.com/openai/v1";
|
||||
let result = input;
|
||||
result = result.replace(/\/openai\/v1\/?$/, "");
|
||||
result = result.replace(/\/v1\/?$/, "");
|
||||
result = result.replace(/\/$/, "");
|
||||
const result = normalizeAzureUrl(input);
|
||||
|
||||
expect(result).toBe("https://workspacename.openai.azure.com");
|
||||
});
|
||||
|
||||
it("should strip /v1 from Azure URL", () => {
|
||||
const input = "https://workspacename.openai.azure.com/v1";
|
||||
let result = input;
|
||||
result = result.replace(/\/openai\/v1\/?$/, "");
|
||||
result = result.replace(/\/v1\/?$/, "");
|
||||
result = result.replace(/\/$/, "");
|
||||
const result = normalizeAzureUrl(input);
|
||||
|
||||
expect(result).toBe("https://workspacename.openai.azure.com");
|
||||
});
|
||||
|
||||
it("should strip trailing slash from Azure URL", () => {
|
||||
const input = "https://workspacename.openai.azure.com/";
|
||||
let result = input;
|
||||
result = result.replace(/\/openai\/v1\/?$/, "");
|
||||
result = result.replace(/\/v1\/?$/, "");
|
||||
result = result.replace(/\/$/, "");
|
||||
const result = normalizeAzureUrl(input);
|
||||
|
||||
expect(result).toBe("https://workspacename.openai.azure.com");
|
||||
});
|
||||
|
||||
it("should handle clean Azure URL without modification", () => {
|
||||
const input = "https://workspacename.openai.azure.com";
|
||||
let result = input;
|
||||
result = result.replace(/\/openai\/v1\/?$/, "");
|
||||
result = result.replace(/\/v1\/?$/, "");
|
||||
result = result.replace(/\/$/, "");
|
||||
const result = normalizeAzureUrl(input);
|
||||
|
||||
expect(result).toBe("https://workspacename.openai.azure.com");
|
||||
});
|
||||
|
||||
it("should strip /openai/v1/ with trailing slash", () => {
|
||||
const input = "https://workspacename.openai.azure.com/openai/v1/";
|
||||
let result = input;
|
||||
result = result.replace(/\/openai\/v1\/?$/, "");
|
||||
result = result.replace(/\/v1\/?$/, "");
|
||||
result = result.replace(/\/$/, "");
|
||||
const result = normalizeAzureUrl(input);
|
||||
|
||||
expect(result).toBe("https://workspacename.openai.azure.com");
|
||||
});
|
||||
|
||||
it("should build correct deployments endpoint for Azure", () => {
|
||||
const input = "https://workspacename.openai.azure.com/openai/v1";
|
||||
let apiUrl = input;
|
||||
apiUrl = apiUrl.replace(/\/openai\/v1\/?$/, "");
|
||||
apiUrl = apiUrl.replace(/\/v1\/?$/, "");
|
||||
apiUrl = apiUrl.replace(/\/$/, "");
|
||||
const apiUrl = normalizeAzureUrl(input);
|
||||
|
||||
const deploymentsUrl = `${apiUrl}/openai/deployments?api-version=2023-05-15`;
|
||||
|
||||
@@ -72,10 +55,7 @@ describe("Azure OpenAI URL Normalization", () => {
|
||||
|
||||
it("should not strip /v1 from middle of path", () => {
|
||||
const input = "https://workspacename.openai.azure.com/v1/something";
|
||||
let result = input;
|
||||
result = result.replace(/\/openai\/v1\/?$/, "");
|
||||
result = result.replace(/\/v1\/?$/, "");
|
||||
result = result.replace(/\/$/, "");
|
||||
const result = normalizeAzureUrl(input);
|
||||
|
||||
// Should only strip trailing /v1, not /v1 in the middle
|
||||
expect(result).toBe("https://workspacename.openai.azure.com/v1/something");
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
getProviderHeaders,
|
||||
getProviderName,
|
||||
type Model,
|
||||
normalizeAzureUrl,
|
||||
} from "@dokploy/server/utils/ai/select-ai-provider";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { z } from "zod";
|
||||
@@ -72,10 +73,8 @@ export const aiRouter = createTRPCRouter({
|
||||
break;
|
||||
case "azure":
|
||||
// Azure OpenAI uses deployments endpoint
|
||||
// Remove trailing /openai/v1 or /v1 if present
|
||||
apiUrl = apiUrl.replace(/\/openai\/v1\/?$/, "");
|
||||
apiUrl = apiUrl.replace(/\/v1\/?$/, "");
|
||||
apiUrl = apiUrl.replace(/\/$/, "");
|
||||
// Normalize the URL to remove trailing /openai/v1 or /v1
|
||||
apiUrl = normalizeAzureUrl(apiUrl);
|
||||
|
||||
if (!input.apiKey)
|
||||
throw new TRPCError({
|
||||
|
||||
@@ -7,6 +7,22 @@ import { createOpenAI } from "@ai-sdk/openai";
|
||||
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
||||
import { createOllama } from "ai-sdk-ollama";
|
||||
|
||||
/**
|
||||
* Normalize Azure OpenAI base URL by removing trailing /openai/v1 or /v1 paths
|
||||
* Azure OpenAI SDK handles path construction internally, so these need to be stripped
|
||||
* to avoid duplicate paths in the final URL (e.g., /v1/v1/chat/completions)
|
||||
*/
|
||||
export function normalizeAzureUrl(url: string): string {
|
||||
let normalized = url;
|
||||
// Remove trailing /openai/v1 if present
|
||||
normalized = normalized.replace(/\/openai\/v1\/?$/, "");
|
||||
// Remove trailing /v1 if present
|
||||
normalized = normalized.replace(/\/v1\/?$/, "");
|
||||
// Remove trailing slash
|
||||
normalized = normalized.replace(/\/$/, "");
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export function getProviderName(apiUrl: string) {
|
||||
if (apiUrl.includes("api.openai.com")) return "openai";
|
||||
if (apiUrl.includes("azure.com")) return "azure";
|
||||
@@ -32,13 +48,7 @@ export function selectAIProvider(config: { apiUrl: string; apiKey: string }) {
|
||||
case "azure": {
|
||||
// Azure OpenAI endpoints should not include /openai/v1 or /v1 at the end
|
||||
// The SDK handles the path construction internally
|
||||
let azureBaseUrl = config.apiUrl;
|
||||
// Remove trailing /openai/v1 if present
|
||||
azureBaseUrl = azureBaseUrl.replace(/\/openai\/v1\/?$/, "");
|
||||
// Remove trailing /v1 if present
|
||||
azureBaseUrl = azureBaseUrl.replace(/\/v1\/?$/, "");
|
||||
// Remove trailing slash
|
||||
azureBaseUrl = azureBaseUrl.replace(/\/$/, "");
|
||||
const azureBaseUrl = normalizeAzureUrl(config.apiUrl);
|
||||
|
||||
return createAzure({
|
||||
apiKey: config.apiKey,
|
||||
|
||||
Reference in New Issue
Block a user