26070eb191
Make the cluster topology configurable so an adopter wired differently (vLLM on both Sparks, port 8000, different container name, no Parakeet) can monitor without forking. Covers the OpenClaw report P4/P5/#6. - VLLM_CONTAINER override (default vllm_node), validated at the boundary and quote_arg-quoted into the swap log-tail + pre-flight validator exec. - DISABLED_SERVICES list: hidden services show no tile and are skipped by status/deep-health/connectivity probes (kills the Parakeet-on-8000 collision). - kind: vllm custom service monitors a second Spark's vLLM via the shared probe_vllm_endpoint; /api/endpoints gains a disabled flag. Swap mechanism intentionally not generalized to raw docker run (that's coordination, roadmap item 4).
92 lines
2.8 KiB
TypeScript
92 lines
2.8 KiB
TypeScript
import { i18n } from './i18n'
|
|
import { sdk } from './sdk'
|
|
import { uiPort } from './utils'
|
|
import { sparkConfigYaml } from './fileModels/sparkConfig.yaml'
|
|
|
|
export const main = sdk.setupMain(async ({ effects }) => {
|
|
console.info(i18n('Starting Spark Control…'))
|
|
|
|
// Reactively read SSH targets from the user-configured yaml file.
|
|
// Changing this file via the "Configure Sparks" action restarts the daemon.
|
|
const cfg = (await sparkConfigYaml.read().const(effects)) ?? {
|
|
spark1_host: '',
|
|
spark1_user: '',
|
|
spark2_host: '',
|
|
spark2_user: '',
|
|
vllm_port: '',
|
|
vllm_container: '',
|
|
disabled_services: '',
|
|
parakeet_host: '',
|
|
parakeet_user: '',
|
|
parakeet_container: '',
|
|
kokoro_host: '',
|
|
kokoro_user: '',
|
|
kokoro_container: '',
|
|
embed_host: '',
|
|
embed_user: '',
|
|
embed_container: '',
|
|
qdrant_host: '',
|
|
qdrant_user: '',
|
|
qdrant_container: '',
|
|
qdrant_collection: '',
|
|
matrix_bridge_user: '',
|
|
open_webui_url: '',
|
|
ngc_api_key: '',
|
|
}
|
|
|
|
return sdk.Daemons.of(effects).addDaemon('primary', {
|
|
subcontainer: await sdk.SubContainer.of(
|
|
effects,
|
|
{ imageId: 'spark-control' },
|
|
sdk.Mounts.of().mountVolume({
|
|
volumeId: 'main',
|
|
subpath: null,
|
|
mountpoint: '/data',
|
|
readonly: false,
|
|
}),
|
|
'spark-control-sub',
|
|
),
|
|
exec: {
|
|
command: ['/app/entrypoint.sh'],
|
|
env: {
|
|
SPARK1_HOST: cfg.spark1_host,
|
|
SPARK1_USER: cfg.spark1_user,
|
|
SPARK2_HOST: cfg.spark2_host,
|
|
SPARK2_USER: cfg.spark2_user,
|
|
VLLM_PORT: cfg.vllm_port,
|
|
VLLM_CONTAINER: cfg.vllm_container,
|
|
DISABLED_SERVICES: cfg.disabled_services,
|
|
PARAKEET_HOST: cfg.parakeet_host,
|
|
PARAKEET_USER: cfg.parakeet_user,
|
|
PARAKEET_CONTAINER: cfg.parakeet_container,
|
|
KOKORO_HOST: cfg.kokoro_host,
|
|
KOKORO_USER: cfg.kokoro_user,
|
|
KOKORO_CONTAINER: cfg.kokoro_container,
|
|
EMBED_HOST: cfg.embed_host,
|
|
EMBED_USER: cfg.embed_user,
|
|
EMBED_CONTAINER: cfg.embed_container,
|
|
QDRANT_HOST: cfg.qdrant_host,
|
|
QDRANT_USER: cfg.qdrant_user,
|
|
QDRANT_CONTAINER: cfg.qdrant_container,
|
|
QDRANT_COLLECTION: cfg.qdrant_collection,
|
|
MATRIX_BRIDGE_USER: cfg.matrix_bridge_user,
|
|
MODELS_OVERRIDES: '/data/models-overrides.yaml',
|
|
SERVICES_OVERRIDES: '/data/services-overrides.yaml',
|
|
CONNECTIVITY_LOG: '/data/connectivity.json',
|
|
OPEN_WEBUI_URL: cfg.open_webui_url,
|
|
NGC_API_KEY: cfg.ngc_api_key,
|
|
BIND_PORT: String(uiPort),
|
|
},
|
|
},
|
|
ready: {
|
|
display: i18n('Web Interface'),
|
|
fn: () =>
|
|
sdk.healthCheck.checkPortListening(effects, uiPort, {
|
|
successMessage: i18n('The web interface is ready'),
|
|
errorMessage: i18n('The web interface is not ready'),
|
|
}),
|
|
},
|
|
requires: [],
|
|
})
|
|
})
|