From 136edfd4c5832f5b756032fe30aac2761f31cd49 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 06:48:47 +0000 Subject: [PATCH] Address code review feedback: Improve URL normalization and remove duplication Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com> --- .../__test__/utils/azure-ai-provider.test.ts | 8 +++++++ apps/dokploy/server/api/routers/ai.ts | 22 ++++++++++--------- .../server/src/utils/ai/select-ai-provider.ts | 13 +++++------ 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/apps/dokploy/__test__/utils/azure-ai-provider.test.ts b/apps/dokploy/__test__/utils/azure-ai-provider.test.ts index 9051c1c24..b356e6ae4 100644 --- a/apps/dokploy/__test__/utils/azure-ai-provider.test.ts +++ b/apps/dokploy/__test__/utils/azure-ai-provider.test.ts @@ -60,4 +60,12 @@ describe("Azure OpenAI URL Normalization", () => { // Should only strip trailing /v1, not /v1 in the middle expect(result).toBe("https://workspacename.openai.azure.com/v1/something"); }); + + it("should handle edge case with multiple trailing /v1", () => { + const input = "https://workspacename.openai.azure.com/openai/v1/v1"; + const result = normalizeAzureUrl(input); + + // Should only strip the last /v1 + expect(result).toBe("https://workspacename.openai.azure.com/openai/v1"); + }); }); diff --git a/apps/dokploy/server/api/routers/ai.ts b/apps/dokploy/server/api/routers/ai.ts index 13d3226c2..937505bb1 100644 --- a/apps/dokploy/server/api/routers/ai.ts +++ b/apps/dokploy/server/api/routers/ai.ts @@ -61,6 +61,18 @@ export const aiRouter = createTRPCRouter({ let response = null; let apiUrl = input.apiUrl; + // Validate API key for providers that require it + if ( + providerName !== "ollama" && + providerName !== "gemini" && + !input.apiKey + ) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "API key must contain at least 1 character(s)", + }); + } + switch (providerName) { case "ollama": response = await fetch(`${apiUrl}/api/tags`, { headers }); @@ -76,11 +88,6 @@ export const aiRouter = createTRPCRouter({ // Normalize the URL to remove trailing /openai/v1 or /v1 apiUrl = normalizeAzureUrl(apiUrl); - if (!input.apiKey) - throw new TRPCError({ - code: "BAD_REQUEST", - message: "API key must contain at least 1 character(s)", - }); // Azure uses deployments endpoint to list models response = await fetch( `${apiUrl}/openai/deployments?api-version=2023-05-15`, @@ -88,11 +95,6 @@ export const aiRouter = createTRPCRouter({ ); break; default: - if (!input.apiKey) - throw new TRPCError({ - code: "BAD_REQUEST", - message: "API key must contain at least 1 character(s)", - }); response = await fetch(`${apiUrl}/models`, { headers }); } diff --git a/packages/server/src/utils/ai/select-ai-provider.ts b/packages/server/src/utils/ai/select-ai-provider.ts index a7f2fe117..a4c55c892 100644 --- a/packages/server/src/utils/ai/select-ai-provider.ts +++ b/packages/server/src/utils/ai/select-ai-provider.ts @@ -13,14 +13,11 @@ import { createOllama } from "ai-sdk-ollama"; * 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; + // Use a single regex to handle all variations in one pass + // This matches: /openai/v1 or /v1 at the end, with optional trailing slash + let normalized = url.replace(/\/(?:openai\/v1|v1)\/?$/, ""); + // Remove any remaining trailing slash + return normalized.replace(/\/$/, ""); } export function getProviderName(apiUrl: string) {