Skip to content

Commit 75aef28

Browse files
Peter MarreckPeter Marreck
authored andcommitted
fix: probe /health before /v1/models when detecting oMLX
oMLX returns 401 on /v1/models without auth. The /health endpoint is unauthenticated and reliably indicates an oMLX server.
1 parent 68c10fd commit 75aef28

1 file changed

Lines changed: 29 additions & 5 deletions

File tree

src/main.zig

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1761,16 +1761,37 @@ fn probeOpenAI(
17611761
transport: embedding_http.Transport,
17621762
base_url: []const u8,
17631763
) ?DetectedServer {
1764-
const url = buildOpenAIModelsUrl(allocator, base_url) catch return null;
1765-
defer allocator.free(url);
1764+
// Try /health first (oMLX returns 401 on /v1/models without auth)
1765+
const health_url = buildUrl(allocator, base_url, "/health") catch return null;
1766+
defer allocator.free(health_url);
17661767

17671768
const headers = [_]std.http.Header{
17681769
.{ .name = "Accept", .value = "application/json" },
17691770
};
17701771

1772+
if (transport.send(transport.ctx, allocator, .{
1773+
.method = "GET",
1774+
.url = health_url,
1775+
.headers = &headers,
1776+
.body = "",
1777+
})) |response| {
1778+
defer allocator.free(response.body);
1779+
if (response.status == 200) {
1780+
return .{
1781+
.url = base_url,
1782+
.dialect = .openai,
1783+
.model_available = true,
1784+
};
1785+
}
1786+
} else |_| {}
1787+
1788+
// Fall back to /v1/models
1789+
const models_url = buildUrl(allocator, base_url, "/v1/models") catch return null;
1790+
defer allocator.free(models_url);
1791+
17711792
const response = transport.send(transport.ctx, allocator, .{
17721793
.method = "GET",
1773-
.url = url,
1794+
.url = models_url,
17741795
.headers = &headers,
17751796
.body = "",
17761797
}) catch return null;
@@ -1785,9 +1806,9 @@ fn probeOpenAI(
17851806
};
17861807
}
17871808

1788-
fn buildOpenAIModelsUrl(allocator: std.mem.Allocator, base_url: []const u8) ![]u8 {
1809+
fn buildUrl(allocator: std.mem.Allocator, base_url: []const u8, path: []const u8) ![]u8 {
17891810
const trimmed = std.mem.trimRight(u8, base_url, "/");
1790-
return std.fmt.allocPrint(allocator, "{s}/v1/models", .{trimmed});
1811+
return std.fmt.allocPrint(allocator, "{s}{s}", .{ trimmed, path });
17911812
}
17921813

17931814
/// Prompts the user with a yes/no question. Returns true for yes.
@@ -5719,6 +5740,9 @@ pub fn runRegexSearch(
57195740

57205741
const OpenAIFallbackMock = struct {
57215742
fn send(_: *anyopaque, allocator: std.mem.Allocator, req: embedding_http.HttpRequest) !embedding_http.HttpResponse {
5743+
if (std.mem.endsWith(u8, req.url, "/health")) {
5744+
return .{ .status = 200, .body = try allocator.dupe(u8, "{\"status\":\"healthy\"}") };
5745+
}
57225746
if (std.mem.endsWith(u8, req.url, "/v1/models")) {
57235747
return .{ .status = 200, .body = try allocator.dupe(u8, "{\"data\":[]}") };
57245748
}

0 commit comments

Comments
 (0)