Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
133 lines
6.4 KiB
TypeScript
133 lines
6.4 KiB
TypeScript
import { FileHelper, z } from '@start9labs/start-sdk'
|
|
|
|
/**
|
|
* Boardroom Map configuration, persisted to the `main` volume as config.json.
|
|
*
|
|
* Written by the StartOS actions (Configure Sparks / Models / Reviewers /
|
|
* Review) and read by the Python orchestrator inside the container, which mounts
|
|
* the same volume at /data and reads /data/config.json. Keep field names in sync
|
|
* with orchestrator/bm_config.py (CONFIG_DEFAULTS).
|
|
*
|
|
* Boardroom Map is a CONTROL PLANE: a GPU-free orchestrator on StartOS that SSHes into
|
|
* one or two DGX Sparks to serve local models and run a panel of sandboxed
|
|
* "reviewer" containers over confidential documents you drop in. There is NO
|
|
* frontier model and NO cloud key — everything stays on your hardware. The only
|
|
* secrets are the Spark SSH key and an optional Hugging Face token (secrets.ts).
|
|
*/
|
|
export const configShape = z.object({
|
|
// --- Spark connection (mirrors LLaMA-Factory / Nightshift) ---
|
|
primarySparkHost: z.string().default(''),
|
|
primarySparkUser: z.string().default('nvidia'),
|
|
sshPort: z.number().int().positive().default(22),
|
|
// Second Spark for extra model capacity. null = single node.
|
|
secondarySparkHost: z.string().nullable().default(null),
|
|
useBothSparks: z.boolean().default(false),
|
|
// Address the head Spark's own preflight checks use to reach the local proxy.
|
|
headInternalHost: z.string().default('127.0.0.1'),
|
|
|
|
// --- Remote execution ---
|
|
remoteWorkDir: z.string().default('/home/nvidia/boardroom-map'),
|
|
|
|
// --- Images (built ON the Sparks; not packed into the s9pk) ---
|
|
servingImage: z.string().default('boardroom-vllm:latest'),
|
|
graderImage: z.string().default('boardroom-grader:latest'),
|
|
|
|
// --- Serving (vLLM on the Sparks) ---
|
|
gpuMemoryUtilization: z.string().default('0.85'),
|
|
maxModelLen: z.number().int().positive().default(32768),
|
|
// vLLM tool-call parser for native function-calling (the reviewer's read-file
|
|
// tool loop relies on it). Must match the served model family — Qwen3 →
|
|
// 'hermes'. Empty disables native tool-calling (reviewers fall back to a
|
|
// JSON-action text protocol).
|
|
toolCallParser: z.string().default('hermes'),
|
|
// LiteLLM router exposing every model alias on one OpenAI-compatible endpoint.
|
|
proxyPort: z.number().int().positive().default(4000),
|
|
// How many distinct models may be co-resident on the HEAD Spark at once. The
|
|
// job runner loads models in WAVES so it never exceeds this — letting you run a
|
|
// panel across more models than fit in GPU memory simultaneously. 1 is safest.
|
|
maxConcurrentModels: z.number().int().positive().default(1),
|
|
|
|
// The MODEL CATALOG: the set of local models the service can serve. Each
|
|
// reviewer (below) references one of these by `alias`. Mirrors
|
|
// bm_config.py CONFIG_DEFAULTS["models"].
|
|
models: z
|
|
.array(
|
|
z.object({
|
|
alias: z.string(),
|
|
hfModel: z.string(),
|
|
// Which Spark serves this model. In `airgapped` network mode all models
|
|
// must be on the head Spark (see networkMode).
|
|
spark: z.enum(['primary', 'secondary']).default('primary'),
|
|
port: z.number().int().positive().default(8001),
|
|
}),
|
|
)
|
|
.default([
|
|
{ alias: 'reviewer-a', hfModel: 'Qwen/Qwen3-32B-FP8', spark: 'primary', port: 8001 },
|
|
]),
|
|
|
|
// --- The review panel: one entry per reviewer ("number of reviews") ---
|
|
// Each reviewer is a model + a persona (the lens it reads through) + an
|
|
// optional temperature. Mirrors bm_config.py CONFIG_DEFAULTS["reviewers"].
|
|
reviewers: z
|
|
.array(
|
|
z.object({
|
|
name: z.string(),
|
|
// Must match one of the model catalog aliases above.
|
|
model: z.string(),
|
|
persona: z.string().nullable().default(''),
|
|
temperature: z.number().nullable().default(null),
|
|
}),
|
|
)
|
|
.default([
|
|
{ name: 'reviewer-1', model: 'reviewer-a', persona: '', temperature: null },
|
|
]),
|
|
|
|
// --- Review job settings ---
|
|
// The rubric: what every reviewer should look for / produce. Layered above
|
|
// each reviewer's persona.
|
|
reviewInstructions: z.string().default(
|
|
'Review the attached document(s). Produce a structured report: a 3-5 sentence ' +
|
|
'summary, the key findings and insights, risks or red flags, open questions, ' +
|
|
'and concrete recommendations. Cite the document and section for each point. ' +
|
|
'Be honest about uncertainty; never invent facts not present in the documents.',
|
|
),
|
|
// Confidentiality posture for the reviewer containers:
|
|
// 'airgapped' — reviewers join an --internal Docker network: they can
|
|
// reach ONLY the on-Spark model proxy, with zero internet
|
|
// egress. Models must be pre-pulled into the Spark's HF
|
|
// cache (no live download). All models must be on the head
|
|
// Spark. Strongest confidentiality.
|
|
// 'local_services' — reviewers may also reach configured LAN services
|
|
// (e.g. SearXNG) and the second Spark. NOTE: this network
|
|
// has egress unless you firewall it — use only when you
|
|
// accept that reviewers can reach the network.
|
|
networkMode: z.enum(['airgapped', 'local_services']).default('airgapped'),
|
|
// SearXNG JSON endpoint, used ONLY in local_services mode to give reviewers a
|
|
// web_search tool. Empty = no web search.
|
|
searxngUrl: z.string().default(''),
|
|
|
|
// --- Synthesis (a local lead reviewer; no frontier model) ---
|
|
synthesisEnabled: z.boolean().default(true),
|
|
// Alias of the model that writes the consolidated report. Empty = first model.
|
|
synthesisModel: z.string().default(''),
|
|
// Optional persona/instructions for the lead reviewer. Empty = built-in default.
|
|
synthesisPersona: z.string().default(''),
|
|
|
|
// --- Document handling ---
|
|
// After a job, wipe the extracted document text from the Sparks. Reports are
|
|
// kept on the StartOS box regardless. Default true for confidentiality.
|
|
wipeRemoteDocs: z.boolean().default(true),
|
|
// Watch /data/inbox and auto-start a review when files land (debounced).
|
|
// Default false: you trigger reviews explicitly with "Run Review".
|
|
autoRunOnDrop: z.boolean().default(false),
|
|
// Name of the per-job Docker network created on the head Spark.
|
|
networkName: z.string().default('boardroom-net'),
|
|
|
|
// --- Auth flags (the secret itself lives in secrets.ts) ---
|
|
hfTokenSet: z.boolean().default(false),
|
|
})
|
|
|
|
export type Config = z.infer<typeof configShape>
|
|
|
|
export const configFile = FileHelper.json('./config.json', configShape)
|