@tiens.nguyen/gonext-local-worker 1.0.114 → 1.0.116
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/gonext-local-worker.mjs +76 -8
- package/package.json +1 -1
package/gonext-local-worker.mjs
CHANGED
|
@@ -1669,6 +1669,40 @@ async function checkOllamaTags(rawBase) {
|
|
|
1669
1669
|
}
|
|
1670
1670
|
}
|
|
1671
1671
|
|
|
1672
|
+
/**
|
|
1673
|
+
* Discover the mlx_lm.server processes actually running on this machine by
|
|
1674
|
+
* parsing `ps` output. Returns deduped `[{ port, modelPath }]` so the model
|
|
1675
|
+
* dropdown reflects live servers instead of a hand-maintained port list.
|
|
1676
|
+
* mlx_lm.server defaults to port 8080 when `--port` is omitted. macOS only.
|
|
1677
|
+
*/
|
|
1678
|
+
async function discoverMlxServers() {
|
|
1679
|
+
if (platform() !== "darwin") return [];
|
|
1680
|
+
let stdout = "";
|
|
1681
|
+
try {
|
|
1682
|
+
({ stdout } = await execFile("ps", ["-Ao", "args="], {
|
|
1683
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
1684
|
+
}));
|
|
1685
|
+
} catch {
|
|
1686
|
+
return [];
|
|
1687
|
+
}
|
|
1688
|
+
const byPort = new Map();
|
|
1689
|
+
for (const raw of stdout.split("\n")) {
|
|
1690
|
+
const line = raw.trim();
|
|
1691
|
+
if (!line) continue;
|
|
1692
|
+
// Match `mlx_lm.server` or `mlx_lm server` (python -m mlx_lm server).
|
|
1693
|
+
if (!/mlx_lm[.\s]server\b/.test(line)) continue;
|
|
1694
|
+
// Ignore our own grep/ps invocations that happen to mention the string.
|
|
1695
|
+
if (/\bgrep\b/.test(line)) continue;
|
|
1696
|
+
const portMatch = line.match(/--port(?:=|\s+)(\d{2,5})/);
|
|
1697
|
+
const port = portMatch ? parseInt(portMatch[1], 10) : 8080;
|
|
1698
|
+
if (!(port > 0)) continue;
|
|
1699
|
+
const modelMatch = line.match(/--model(?:=|\s+)(\S+)/);
|
|
1700
|
+
const modelPath = modelMatch ? modelMatch[1] : "";
|
|
1701
|
+
if (!byPort.has(port)) byPort.set(port, { port, modelPath });
|
|
1702
|
+
}
|
|
1703
|
+
return [...byPort.values()];
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1672
1706
|
async function checkOpenAiModels(base, apiKey, source) {
|
|
1673
1707
|
const endpoint = `${base}/models`;
|
|
1674
1708
|
const headers = {};
|
|
@@ -1842,13 +1876,6 @@ async function runLocalHealthJob(job) {
|
|
|
1842
1876
|
if (!dedup.has(m.value)) dedup.set(m.value, m);
|
|
1843
1877
|
}
|
|
1844
1878
|
}
|
|
1845
|
-
// Support both single mlxOpenAiBaseUrl and multi-port mlxOpenAiBaseUrls
|
|
1846
|
-
const mlxRootSingle = normalizeOpenAiV1Root(payload?.mlxOpenAiBaseUrl);
|
|
1847
|
-
const mlxRootsMulti = Array.isArray(payload?.mlxOpenAiBaseUrls)
|
|
1848
|
-
? payload.mlxOpenAiBaseUrls.map(normalizeOpenAiV1Root).filter(Boolean)
|
|
1849
|
-
: [];
|
|
1850
|
-
const mlxRoots = mlxRootsMulti.length > 0 ? mlxRootsMulti : (mlxRootSingle ? [mlxRootSingle] : []);
|
|
1851
|
-
const mlxRoot = mlxRoots[0] || null;
|
|
1852
1879
|
const targetWorkerHostId =
|
|
1853
1880
|
typeof payload?.targetWorkerHostId === "string"
|
|
1854
1881
|
? payload.targetWorkerHostId.trim()
|
|
@@ -1857,6 +1884,30 @@ async function runLocalHealthJob(job) {
|
|
|
1857
1884
|
typeof payload?.targetWorkerHostName === "string"
|
|
1858
1885
|
? payload.targetWorkerHostName.trim()
|
|
1859
1886
|
: "";
|
|
1887
|
+
// MLX servers are discovered dynamically from running mlx_lm.server
|
|
1888
|
+
// processes (macOS), so the dropdown reflects reality with no saved port
|
|
1889
|
+
// list. Only discover when a worker host is targeted so the encoded models
|
|
1890
|
+
// are host-scoped (routable). Each server is probed at 127.0.0.1:<port>/v1.
|
|
1891
|
+
const discoveredMlx = targetWorkerHostId ? await discoverMlxServers() : [];
|
|
1892
|
+
const discoveredByPort = new Map(discoveredMlx.map((s) => [s.port, s]));
|
|
1893
|
+
const mlxRootsDiscovered = discoveredMlx.map(
|
|
1894
|
+
(s) => `http://127.0.0.1:${s.port}/v1`
|
|
1895
|
+
);
|
|
1896
|
+
// Fall back to any explicitly provided base (e.g. a remote host) so
|
|
1897
|
+
// non-local MLX endpoints still work when no local process is found.
|
|
1898
|
+
const mlxRootSingle = normalizeOpenAiV1Root(payload?.mlxOpenAiBaseUrl);
|
|
1899
|
+
const mlxRootsMulti = Array.isArray(payload?.mlxOpenAiBaseUrls)
|
|
1900
|
+
? payload.mlxOpenAiBaseUrls.map(normalizeOpenAiV1Root).filter(Boolean)
|
|
1901
|
+
: [];
|
|
1902
|
+
const mlxRoots =
|
|
1903
|
+
mlxRootsDiscovered.length > 0
|
|
1904
|
+
? mlxRootsDiscovered
|
|
1905
|
+
: mlxRootsMulti.length > 0
|
|
1906
|
+
? mlxRootsMulti
|
|
1907
|
+
: mlxRootSingle
|
|
1908
|
+
? [mlxRootSingle]
|
|
1909
|
+
: [];
|
|
1910
|
+
const mlxRoot = mlxRoots[0] || null;
|
|
1860
1911
|
let mlxNative = null;
|
|
1861
1912
|
const modelPathChecksRaw = Array.isArray(payload?.modelPathChecks)
|
|
1862
1913
|
? payload.modelPathChecks
|
|
@@ -1892,12 +1943,29 @@ async function runLocalHealthJob(job) {
|
|
|
1892
1943
|
// Merge all per-port results
|
|
1893
1944
|
const mlxModelDedup = new Map();
|
|
1894
1945
|
let mlxHttpOnline = false;
|
|
1946
|
+
const portsWithModels = new Set();
|
|
1895
1947
|
for (const r of mlxHttpResults) {
|
|
1896
1948
|
if (r.online) mlxHttpOnline = true;
|
|
1897
1949
|
for (const m of r.models) {
|
|
1950
|
+
const pm = String(m.value ?? "").match(/@@port:(\d+)/);
|
|
1951
|
+
if (pm) portsWithModels.add(parseInt(pm[1], 10));
|
|
1898
1952
|
if (!mlxModelDedup.has(m.value)) mlxModelDedup.set(m.value, m);
|
|
1899
1953
|
}
|
|
1900
1954
|
}
|
|
1955
|
+
// A discovered server may be booting (process up, /v1/models not ready).
|
|
1956
|
+
// Synthesize an entry from its `--model` path so it is still visible.
|
|
1957
|
+
for (const { port, modelPath } of discoveredByPort.values()) {
|
|
1958
|
+
if (portsWithModels.has(port) || !modelPath) continue;
|
|
1959
|
+
const hostSuffix = targetWorkerHostId ? `@@host:${targetWorkerHostId}` : "";
|
|
1960
|
+
const value = `mlx:${modelPath}${hostSuffix}@@port:${port}`;
|
|
1961
|
+
if (mlxModelDedup.has(value)) continue;
|
|
1962
|
+
const label = targetWorkerHostName || sourceLabelFromBase(`http://127.0.0.1:${port}`);
|
|
1963
|
+
mlxModelDedup.set(value, {
|
|
1964
|
+
id: targetWorkerHostId ? `${modelPath}@@${targetWorkerHostId}` : modelPath,
|
|
1965
|
+
name: `${modelPath} (${label}:${port}) — starting…`,
|
|
1966
|
+
value,
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1901
1969
|
const mlxHttp = mlxRoots.length > 0 ? {
|
|
1902
1970
|
online: mlxHttpOnline,
|
|
1903
1971
|
endpoint: mlxHttpResults[0]?.endpoint,
|
|
@@ -1905,7 +1973,7 @@ async function runLocalHealthJob(job) {
|
|
|
1905
1973
|
} : null;
|
|
1906
1974
|
|
|
1907
1975
|
const wantNativeFallback =
|
|
1908
|
-
mlxRoot &&
|
|
1976
|
+
(targetWorkerHostId || mlxRoot) &&
|
|
1909
1977
|
payload?.mlxNativeFallback !== false &&
|
|
1910
1978
|
platform() === "darwin" &&
|
|
1911
1979
|
(!mlxHttp?.online || (mlxHttp?.models?.length ?? 0) === 0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiens.nguyen/gonext-local-worker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.116",
|
|
4
4
|
"description": "Polls GoNext cloud API for async local LLM jobs and runs them against Ollama/OpenAI-compatible servers on this Mac",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|