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 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-15 16:39:09 +03:30
parent 2ac1b6aa18
commit 39881a20eb
@@ -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
}
}
/// <summary>
/// Resolve the chat-completions URL from a BYOK endpoint. Accepts a base URL (we append the path),
/// a base already ending in <c>/v1</c>, or the full <c>…/chat/completions</c> URL pasted as-is — so
/// a user who enters the complete gateway URL doesn't get a doubled path.
/// </summary>
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 })