mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-26 00:52:18 +03:00
Landed with the design call resolved per the owner's pick — **option 1**: the synced store is now endpoint-agnostic (persistDiscoveredModels and managedModelImport no longer drop non-chat models at write time), and chat selectability moved to read time (auto-pool expansion in autoStrategy applies filterChatSelectableModels; the models-route projection already had its chatOnly filter). Your discovery test now passes end-to-end (3/3): /api/show capabilities persist per connection and image/embedding requests route through the advertising host. Reconciliation notes: conflicted areas merged onto the current tip (adobe discovery import, requestedModel preflight signature, resolvedProvider fast-path coexists with the synced-route override — explicit resolution wins); carried base-red drains (#10055 memoization, #11071 test variants) dropped as already-landed; the managed-model-import exclusion test was propagated to the new contract (image/video models persist; the read filter still hides them from chat pickers — pinned by a new assertion). Full battery: 205/206 focused (the one red is a confirmed periodic-timer timing flake on the loaded devbox — 20/20 isolated), autoCombo vitest 30/30, combo suites 46/46, gates + typecheck clean. Thank you @yourspraveen — the capability probe + routing design was right; it just needed the store contract opened up. Fixes #11087.
99 lines
3.1 KiB
JavaScript
99 lines
3.1 KiB
JavaScript
/**
|
|
* Minimal systemd sd_notify integration (sd_notify(3) protocol).
|
|
*
|
|
* Node's stable API has no AF_UNIX datagram socket support (node:dgram is
|
|
* udp4/udp6 only), so notifications are sent by spawning the `systemd-notify`
|
|
* binary — present on every systemd host, no extra dependency.
|
|
*
|
|
* Everything is guarded: without a NOTIFY_SOCKET (plain terminal, Docker,
|
|
* Electron, Windows) the notifier is a no-op and costs nothing. Set
|
|
* OMNIROUTE_DISABLE_SD_NOTIFY=1 to force-disable even under systemd.
|
|
*
|
|
* A watchdog keep-alive interval lives in the main event loop of the process
|
|
* that runs it: if that loop is ever blocked (frozen server, cf. the cold
|
|
* /v1/models rebuild freeze), the pings stop and systemd kills the service
|
|
* after WatchdogSec=.
|
|
*/
|
|
|
|
import { spawn } from "node:child_process";
|
|
|
|
export const SD_NOTIFY_BINARY = "systemd-notify";
|
|
export const SD_NOTIFY_SOCKET_ENV = "NOTIFY_SOCKET";
|
|
export const SD_NOTIFY_DISABLE_ENV = "OMNIROUTE_DISABLE_SD_NOTIFY";
|
|
// Ping every 60s — satisfies any systemd WatchdogSec= >= 120s (systemd
|
|
// requires keep-alive pings at most every WatchdogSec/2).
|
|
export const SD_NOTIFY_WATCHDOG_INTERVAL_MS = 60_000;
|
|
|
|
export function isSystemdNotifyEnabled(env = process.env) {
|
|
return Boolean(env[SD_NOTIFY_SOCKET_ENV]) && env[SD_NOTIFY_DISABLE_ENV] !== "1";
|
|
}
|
|
|
|
export function buildNotifyMessage(kind) {
|
|
switch (kind) {
|
|
case "ready":
|
|
return "READY=1";
|
|
case "watchdog":
|
|
return "WATCHDOG=1";
|
|
case "stopping":
|
|
return "STOPPING=1";
|
|
default:
|
|
throw new Error(`[omniroute][sd_notify] unknown message kind: ${kind}`);
|
|
}
|
|
}
|
|
|
|
export function createSystemdNotifier({
|
|
env = process.env,
|
|
binary = SD_NOTIFY_BINARY,
|
|
watchdogIntervalMs = SD_NOTIFY_WATCHDOG_INTERVAL_MS,
|
|
spawnFn = spawn,
|
|
onWarn = (message) => console.warn(message),
|
|
} = {}) {
|
|
const enabled = isSystemdNotifyEnabled(env);
|
|
let disabled = false;
|
|
let watchdogTimer = null;
|
|
|
|
const send = (kind) => {
|
|
if (!enabled || disabled) return;
|
|
const child = spawnFn(binary, [buildNotifyMessage(kind)], { env, stdio: "ignore" });
|
|
// Never let a hung systemd-notify keep the process alive.
|
|
child.unref?.();
|
|
child.on("error", (err) => {
|
|
// A failed send means systemd never sees the keep-alive: the service
|
|
// would be killed as unhealthy anyway, so disabling loudly (one
|
|
// warning) is safer than spamming errors forever.
|
|
disabled = true;
|
|
if (watchdogTimer) {
|
|
clearInterval(watchdogTimer);
|
|
watchdogTimer = null;
|
|
}
|
|
onWarn(
|
|
`[omniroute][sd_notify] failed to send '${kind}' (${err?.code ?? err?.message ?? err}); sd_notify disabled for this process`
|
|
);
|
|
});
|
|
};
|
|
|
|
return {
|
|
enabled,
|
|
ready() {
|
|
send("ready");
|
|
},
|
|
watchdog() {
|
|
send("watchdog");
|
|
},
|
|
stopping() {
|
|
send("stopping");
|
|
},
|
|
startWatchdog() {
|
|
if (!enabled || disabled || watchdogTimer) return;
|
|
watchdogTimer = setInterval(() => send("watchdog"), watchdogIntervalMs);
|
|
watchdogTimer.unref?.();
|
|
},
|
|
dispose() {
|
|
if (watchdogTimer) {
|
|
clearInterval(watchdogTimer);
|
|
watchdogTimer = null;
|
|
}
|
|
},
|
|
};
|
|
}
|