Files
spark-control/package/startos/main.ts
T
Grant 75fd0846b4 v0.2.3 - Per-model Advanced settings + catalog-add for downloaded models
Backend:
- overrides.py: read/write /data/models-overrides.yaml (knobs + custom entries)
- apply_knobs_to_args(): strip matching flags from bundled vllm_args and append knob values, so knob changes properly override bundled defaults
- extract_knobs_from_args(): seed UI knob values from bundled args so the Advanced dialog has correct starting state
- models.py: load_catalog merges overrides on top of bundled yaml
- GET /api/models returns effective_knobs per model
- PUT /api/models/{key}/knobs persists knob changes
- POST /api/models adds a custom catalog entry
- DELETE /api/models/{key} removes a custom entry (bundled models cannot be deleted)
- swap_manager.reload_catalog() called after each mutation so swaps see latest

Frontend:
- New 'Advanced' button on every card opens a modal dialog: max-model-len input, gpu-memory-utilization slider, three optimization checkboxes (fastsafetensors, prefix caching, FP8 KV cache). Save persists; Cancel discards. Custom models also have a Delete button.
- After a successful download, automatically open the 'Add to catalog' dialog pre-filled with the repo, with the same knob defaults — user just enters key, display name, and clicks Save.
- Custom catalog entries are tagged with a blue 'custom' pill on the card.

Package: bump 0.2.3:0; main.ts sets MODELS_OVERRIDES=/data/models-overrides.yaml so overrides persist on the StartOS volume.
2026-05-12 11:30:47 -05:00

64 lines
1.9 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: '',
parakeet_host: '',
parakeet_user: '',
parakeet_container: '',
magpie_host: '',
magpie_user: '',
magpie_container: '',
}
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,
PARAKEET_HOST: cfg.parakeet_host,
PARAKEET_USER: cfg.parakeet_user,
PARAKEET_CONTAINER: cfg.parakeet_container,
MAGPIE_HOST: cfg.magpie_host,
MAGPIE_USER: cfg.magpie_user,
MAGPIE_CONTAINER: cfg.magpie_container,
MODELS_OVERRIDES: '/data/models-overrides.yaml',
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: [],
})
})