From 39881a20ebf9f35d002308f33d14c785dd1a59bf Mon Sep 17 00:00:00 2001 From: "soroush.asadi" Date: Mon, 15 Jun 2026 16:39:09 +0330 Subject: [PATCH] Fix BYOK endpoint URL: accept full chat-completions URLs, not just base URLs The OpenAI-compatible adapter unconditionally appended /v1/chat/completions to the configured endpoint, so a BYOK config whose endpoint is the full gateway URL (e.g. https://host/v1/chat/completions) produced a doubled path and failed. ResolveChatUrl now uses the URL as-is when it already targets /chat/completions, appends /chat/completions to a base ending in /v1, and otherwise appends /v1/chat/completions. Co-Authored-By: Claude Opus 4.8 --- .../Ai/ModelClients.cs | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Modules/TeamUp.Modules.Integrations/Ai/ModelClients.cs b/src/Modules/TeamUp.Modules.Integrations/Ai/ModelClients.cs index c312474..c0a765d 100644 --- a/src/Modules/TeamUp.Modules.Integrations/Ai/ModelClients.cs +++ b/src/Modules/TeamUp.Modules.Integrations/Ai/ModelClients.cs @@ -50,8 +50,7 @@ internal sealed class OpenAiCompatibleModelClient(HttpClient http) : IModelClien var stopwatch = Stopwatch.StartNew(); try { - var baseUrl = (request.Endpoint ?? "https://api.openai.com").TrimEnd('/'); - using var message = new HttpRequestMessage(HttpMethod.Post, $"{baseUrl}/v1/chat/completions"); + using var message = new HttpRequestMessage(HttpMethod.Post, ResolveChatUrl(request.Endpoint)); if (!string.IsNullOrEmpty(request.ApiKey)) { message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", request.ApiKey); @@ -110,6 +109,24 @@ internal sealed class OpenAiCompatibleModelClient(HttpClient http) : IModelClien } } + /// + /// Resolve the chat-completions URL from a BYOK endpoint. Accepts a base URL (we append the path), + /// a base already ending in /v1, or the full …/chat/completions URL pasted as-is — so + /// a user who enters the complete gateway URL doesn't get a doubled path. + /// + private static string ResolveChatUrl(string? endpoint) + { + var url = (endpoint ?? "https://api.openai.com").Trim().TrimEnd('/'); + if (url.Contains("/chat/completions", StringComparison.OrdinalIgnoreCase)) + { + return url; + } + + return url.EndsWith("/v1", StringComparison.OrdinalIgnoreCase) + ? $"{url}/chat/completions" + : $"{url}/v1/chat/completions"; + } + private static object[] BuildMessages(ModelRequest request) { if (request.Messages is not { Count: > 0 })