8d839e3714
- Add redaction gateway (redaction_gateway.py, redaction/ scrub + tests) - Add embeddings proxy and spark_embed service (Dockerfile + main.py) - Expand audio_proxy with speaker-aware handling; deep_health/health/server updates - Package: configureSparks action + sparkConfig model updates, manifest/main wiring - Docs: AUDIO_API, EMBEDDINGS, REDACTION_GATEWAY; HANDOFF and runbook/known-issues refresh
166 lines
5.7 KiB
TypeScript
166 lines
5.7 KiB
TypeScript
import { sdk } from '../sdk'
|
|
import { sparkConfigYaml } from '../fileModels/sparkConfig.yaml'
|
|
|
|
const { InputSpec, Value } = sdk
|
|
|
|
const inputSpec = InputSpec.of({
|
|
spark1_host: Value.text({
|
|
name: 'Spark 1 hostname or IP',
|
|
description:
|
|
'The head node of your DGX Spark cluster — the one that has ~/spark-vllm-docker cloned and runs the vLLM container. Enter its LAN IP (recommended) or hostname.',
|
|
required: true,
|
|
default: null,
|
|
placeholder: 'e.g. 192.168.1.10',
|
|
masked: false,
|
|
}),
|
|
spark1_user: Value.text({
|
|
name: 'Spark 1 SSH user',
|
|
description:
|
|
'The user account on Spark 1 to SSH in as — whatever you log in as when you ssh into it manually.',
|
|
required: true,
|
|
default: null,
|
|
placeholder: 'your SSH username',
|
|
masked: false,
|
|
}),
|
|
spark2_host: Value.text({
|
|
name: 'Spark 2 hostname or IP',
|
|
description:
|
|
'The worker node of your DGX Spark cluster (also runs always-on services like Parakeet and Kokoro). Enter its LAN IP or hostname.',
|
|
required: true,
|
|
default: null,
|
|
placeholder: 'e.g. 192.168.1.11',
|
|
masked: false,
|
|
}),
|
|
spark2_user: Value.text({
|
|
name: 'Spark 2 SSH user',
|
|
description:
|
|
'The user account on Spark 2 to SSH in as. Usually the same as Spark 1.',
|
|
required: true,
|
|
default: null,
|
|
placeholder: 'your SSH username',
|
|
masked: false,
|
|
}),
|
|
parakeet_host: Value.text({
|
|
name: 'Parakeet host (optional)',
|
|
description:
|
|
'Override the host running the Parakeet STT container. Leave blank if Parakeet runs on Spark 2 — that\'s the default. Set this if you run Parakeet on Spark 1 or a different machine.',
|
|
required: false,
|
|
default: null,
|
|
placeholder: 'leave blank to use Spark 2',
|
|
masked: false,
|
|
}),
|
|
parakeet_container: Value.text({
|
|
name: 'Parakeet container name (optional)',
|
|
description:
|
|
'Docker container name for Parakeet. Defaults to "parakeet-asr" — change only if you named yours something else.',
|
|
required: false,
|
|
default: null,
|
|
placeholder: 'parakeet-asr',
|
|
masked: false,
|
|
}),
|
|
kokoro_host: Value.text({
|
|
name: 'Kokoro host (optional)',
|
|
description:
|
|
'Override the host running the Kokoro TTS container. Leave blank if Kokoro runs on Spark 2.',
|
|
required: false,
|
|
default: null,
|
|
placeholder: 'leave blank to use Spark 2',
|
|
masked: false,
|
|
}),
|
|
kokoro_container: Value.text({
|
|
name: 'Kokoro container name (optional)',
|
|
description:
|
|
'Docker container name for Kokoro. Defaults to "kokoro-tts".',
|
|
required: false,
|
|
default: null,
|
|
placeholder: 'kokoro-tts',
|
|
masked: false,
|
|
}),
|
|
embed_host: Value.text({
|
|
name: 'Embedding server host (optional)',
|
|
description:
|
|
'Override the host running the spark-embed container (bge-m3 dense embeddings + reranker). Leave blank if it runs on Spark 2.',
|
|
required: false,
|
|
default: null,
|
|
placeholder: 'leave blank to use Spark 2',
|
|
masked: false,
|
|
}),
|
|
embed_container: Value.text({
|
|
name: 'Embedding container name (optional)',
|
|
description: 'Docker container name for the embedding server. Defaults to "spark-embed".',
|
|
required: false,
|
|
default: null,
|
|
placeholder: 'spark-embed',
|
|
masked: false,
|
|
}),
|
|
qdrant_host: Value.text({
|
|
name: 'Qdrant host (optional)',
|
|
description:
|
|
'Override the host running the Qdrant vector database. Leave blank if it runs on Spark 2.',
|
|
required: false,
|
|
default: null,
|
|
placeholder: 'leave blank to use Spark 2',
|
|
masked: false,
|
|
}),
|
|
qdrant_container: Value.text({
|
|
name: 'Qdrant container name (optional)',
|
|
description: 'Docker container name for Qdrant. Defaults to "qdrant".',
|
|
required: false,
|
|
default: null,
|
|
placeholder: 'qdrant',
|
|
masked: false,
|
|
}),
|
|
qdrant_collection: Value.text({
|
|
name: 'Default Qdrant collection (optional)',
|
|
description:
|
|
'Default collection name used by /api/search when a request does not specify one. Leave blank to require callers to pass a collection.',
|
|
required: false,
|
|
default: null,
|
|
placeholder: 'e.g. crm_chunks',
|
|
masked: false,
|
|
}),
|
|
open_webui_url: Value.text({
|
|
name: 'Open WebUI URL (optional)',
|
|
description:
|
|
'If you also run Open WebUI on your LAN, paste its URL here. Spark Control will then show a one-click "Open chat" button next to the current model so you can jump straight to it.',
|
|
required: false,
|
|
default: null,
|
|
placeholder: 'e.g. https://open-webui.yourserver.local',
|
|
masked: false,
|
|
}),
|
|
ngc_api_key: Value.text({
|
|
name: 'NGC API key (optional)',
|
|
description:
|
|
'NVIDIA NGC personal API key — needed to install NIM containers (Parakeet, etc.) from nvcr.io. Get one free at https://ngc.nvidia.com/setup/personal-key. Stored only on this Start9 server; passed to docker as the NGC_API_KEY env var when installing NIM services. (Kokoro TTS is Apache 2.0 and does not need an NGC key.)',
|
|
required: false,
|
|
default: null,
|
|
placeholder: 'starts with "nvapi-..."',
|
|
masked: true,
|
|
}),
|
|
})
|
|
|
|
export const configureSparks = sdk.Action.withInput(
|
|
'configure-sparks',
|
|
async () => ({
|
|
name: 'Configure Sparks',
|
|
description: 'Set the hostnames and SSH users for your two Spark nodes.',
|
|
warning: null,
|
|
visibility: 'enabled',
|
|
allowedStatuses: 'any',
|
|
group: null,
|
|
}),
|
|
async () => inputSpec,
|
|
async ({ effects }) => {
|
|
const cfg = await sparkConfigYaml.read().once()
|
|
return cfg ?? null
|
|
},
|
|
async ({ effects, input }) => {
|
|
// Optional fields come through as `null`; coerce to empty string for the schema.
|
|
const normalized = Object.fromEntries(
|
|
Object.entries(input).map(([k, v]) => [k, v ?? '']),
|
|
) as Record<string, string>
|
|
await sparkConfigYaml.merge(effects, normalized)
|
|
return null
|
|
},
|
|
)
|