Typical Python SDK soup
client = OpenAI(...)
resp = client.chat.completions.create(...)
# separate train script, eval notebook,
# CI secrets, ad-hoc shell glue…
SparkLang
A language beats another Python SDK because one reviewable
.spark file covers
train → status → expect, with dry-run fixtures you can run in CI
without API keys.
For solo and small teams who want reviewable train-job pipelines in PRs — without maintaining SDK glue.
Offline-first by default:
./spark --dry-run.
Live submit POSTs to a trainer you run —
SPARK_TRAIN_URL.
Reference methods (CPU only):
spark_distill_cpu,
spark_pref_pack,
spark_playbook_fit
— real weights / packs, not LoRA, not a voice GPU.
Library glue scatters prompts, training scripts, and eval harnesses
across repos. A .spark program is one
reviewable surface — same file for dry-run CI and live ops.
client = OpenAI(...)
resp = client.chat.completions.create(...)
# separate train script, eval notebook,
# CI secrets, ad-hoc shell glue…
model train dataset "data.jsonl" base "base" out "out/job-1" backend "http" -> job
model status "job-1" -> status
expect contains job fixture "…/want_accepted.txt"
expect contains status fixture "…/want_succeeded.txt"
# ./spark --dry-run train_eval.spark
./spark --dry-run uses offline
fixtures — no keys, no network. Pass
--live only when you want real
calls.
./spark --dry-run examples/model_train.spark
model train /
model build submit via
backend "http" to your trainer at
SPARK_TRAIN_URL
(POST …/jobs,
GET …/jobs/{id}). Dry-run uses
fixtures; live needs a service that implements the
Trainer HTTP contract
— in-repo reference:
tools/spark-train-ref
ships three CPU methods:
spark_distill_cpu,
spark_pref_pack,
spark_playbook_fit
(real weights.pt /
pref_pack.json+ranker /
playbooks.json+router —
not LoRA). Captures:
live-train-capture.txt,
live-train-methods-capture.txt.
model train dataset "…" base "…" out "…" backend "http" -> job
model status "job-id" -> status
After train, assert bound job and status fields with
expect contains … fixture
(exit 0/1) — gate of this job, not unrelated gateway
aliases.
expect contains job fixture "…/want_accepted.txt"
expect contains status fixture "…/want_succeeded.txt"
Dry-run writes fixture artifact paths (no GPU). Live POSTs to the
trainer at
SPARK_TRAIN_URL
(backend "http"), or an allowlisted
local-yield unit.
# examples/train_eval.spark — dry-run first
model train dataset "examples/fixtures/train/dataset.jsonl" base "fixture-base" out "out/train/job-dry-001" backend "http" -> job
model status "job-dry-001" -> status
expect contains job fixture "examples/fixtures/train/want_accepted.txt"
expect contains status fixture "examples/fixtures/train/want_succeeded.txt"
Fail path:
examples/train_eval_fail.spark
(exit 1). Live HTTP proof (three CPU methods, via
./spark --live /
./spark-train-http --method):
live-train-methods-capture.txt.
Model training docs ·
Build a Model ·
What's real today.
IDE ops, embed/retrieve, voice demos, HTTP get/post, and network/browser surfaces live in the language reference and what's real today — not the homepage thesis.
Follow the trail: install → first dry-run → train→status→expect → gate a job.
Install the runtime and verify with
./spark --dry-run.
A minimal train → status → expect loop you can dry-run in CI.
Write train_eval.spark →Orchestrate jobs, expect gates, classify, extract — the train→status→expect primitives.
Quick tour →Submit a dry-run train job, poll status, then expect pass/fail.
Open wizard →# Dry-run train → status → expect — no network, no API key
./spark --dry-run examples/train_eval.spark
# Optional live submit — your trainer at SPARK_TRAIN_URL
export SPARK_TRAIN_URL=http://127.0.0.1:8090/v1
./spark --live examples/model_train.spark
See programming guide and language reference.