Skip to content

Releases: jonpol01/d1a

D1A 0.6.0

Choose a tag to compare

@github-actions github-actions released this 20 Oct 08:45
v0.6.0
9bf2230

D1A also chats. An instruction-tuned D1A build served with its adapter unmerged answers OpenAI chat and embedding requests from the same model that makes decisions, and keeps the prompts it has seen, so an agent's next step costs seconds instead of a full read of its prompt. Decisions can depend on earlier answers, agents get a guide and a tool gate, and every decision stays as it was in 0.5.0.

What's new

  • One model for decisions and chat. d1a.serving.serve answers POST /v1/chat/completions (tools, JSON mode, streaming) and POST /v1/embeddings on an unmerged -it MLX export, so OpenAI clients (mem0, n8n, chat UIs) and D1A's decisions share one model. A decision that arrives during a chat waits for one token or one prompt chunk, not the whole chat. See docs/USAGE.md, "Chat with D1A".
  • A prompt cache for agents. A chat that continues an earlier prompt prefills only its new tokens: on D1A-E4B-it, an agent step on a 20k-token prompt takes 1–1.7 s instead of 33 s (M1 Max), and 2–3 s instead of 39 s (M4).
  • Conditional questions. ask_if asks a question only when earlier answers meet a condition, in one request.
  • D1A for agents. docs/AGENTS_GUIDE.md and python -m d1a.agents.demo show intake, tool gating, judging and routing; d1a.agents.claude_code_gate is a Claude Code hook that asks D1A before a tool call runs and falls back to asking you on any failure.
  • Instruction-tuned models. D1A-E4B-it and D1A-E2B-it v0.6 load and serve correctly (they now get the <bos> they were trained with), and v0.6.1 adds a routing temperature to the default checkpoints.
  • More control over self-learning, all off by default.
    • Decode rules: a learned rule moves a choice only where its condition holds, and the quality gate checks it fired everywhere it should.
    • Flags can run in shadow mode, with thresholds that track the live scores.
    • Recalibration can now work per option, hold out the newest outcomes, and keep the model's own answer for held options such as severity P0/P1.
  • Logs you can read. The server prints a line per chat with its cached tokens, and docs/CONFIGURATION.md has a Logs section: what is written, and that none of it holds request or answer text.

Added

  • The model server prints a line per chat: prompt tokens, how many came from the prompt cache, completion tokens and time (#306). It holds counts only, never text. D1A_CHAT_LOG=0 turns it off. docs/CONFIGURATION.md gains a Logs section: what the server writes, what it never holds, and where the Mac mini keeps it.
  • The OpenAI chat endpoint keeps a prompt cache (#304). A chat that continues an earlier one prefills only its new tokens: an agent's next step, a conversation's next turn, or a new session with the same system prompt. This covers up to 16 prompts in 1 GiB (D1A_CHAT_CACHE_MB; 0 turns it off). With D1A-E4B-it on an M1 Max, an agent step on a 20k-token prompt takes 1–1.7 s instead of 33 s. usage.prompt_tokens_details.cached_tokens reports the tokens taken from the cache.
  • python -m d1a.agents.demo: one task through an agent's day (intake, gate, judge, route). Each step is a D1A call with its top answers and the fail-safe advice.
  • docs/AGENTS_GUIDE.md: what D1A decides for agents, the three ways to plug it in (MCP, the Claude Code gate hook, Python), real demo output, and the measured numbers with their limits. The README gains a "For Agents" section.
  • d1a.agents.claude_code_gate, a Claude Code PreToolUse hook: D1A's gate preset decides allow, ask or deny before an agent's tool call runs. It fails safe: "allow" is passed on only with D1A_GATE_ALLOW=1, and an unreachable D1A asks a person. New settings: D1A_GATE_CARD, D1A_GATE_ALLOW.
  • Tests prove agents can use D1A: a real MCP client lists and calls the MCP server's tools over stdio on a tiny trained checkpoint, and CI installs the mcp extra so the MCP server is tested at all.
  • Conditional questions (#36): a question may carry ask_if ({question id: condition}) and is answered only when the answers it names meet their conditions (a choice's option keys, a noul's true/false, a score's range), in later passes of the same request. Otherwise it is reported as skipped. It is never model input: a request without ask_if is answered exactly as before. The server, D1A.decide and the MCP/clients' pass-through take it; permute and separate refuse it.
  • Learned flags can run in shadow mode (flags.<name>.mode = "shadow"): the flag is logged but not served, so the response is unchanged (#297). A tracked threshold (flags.<name>.track) is refit by the loop's tick from logged scores without labels, clamped, with a flag_alert notification when the recent flag rate runs above alert_rate or t sits at a clamp.
  • Self-learning settings, all off by default (#294): promotion.form = "vector" (per-option recalibration of choice questions), promotion.split = "time" (the gate holds out the newest outcomes), and promotion.serve_choice with promotion.hold (serve the recalibrated top option, but keep the model's own answer for held options such as severity P0/P1). Flags and rules may name a local-path run.
  • scripts/quality_gate.py --sequential (#286): the base server answers every request, stops, then the head server does, so only one model is in memory at a time. The Mac mini (32 GB, with its other services) cannot hold two, which stopped the
    #285 baseline gate there. The latency floor is measured the same way, so the latency check still compares like with like.
  • OpenAI-compatible chat and embeddings on d1a.serving.serve (#274), so apps written for the OpenAI API (mem0, n8n, chat UIs) can use D1A's model instead of a separate chat server.
    • POST /v1/chat/completions answers with the served model's adapter switched off: an export made with --unmerged on an instruction-tuned base (D1A-E2B-it), whose base is then a chat model. It runs on the model thread between decision requests and supports streaming (token by token), tools (Gemma 4 tool calls as OpenAI tool_calls), stop and JSON mode.
    • POST /v1/embeddings serves a GGUF embedder through llama.cpp (D1A_EMBED_GGUF, the new embed extra).
    • /v1/models also lists the models in OpenAI form beside the TypeSafe listing.
    • New settings: D1A_CHAT_NAME, D1A_EMBED_GGUF, D1A_EMBED_NAME. Guide: "Chat with D1A" in docs/USAGE.md.
    • An --unmerged export also carries the base's generation_config.json, so chat stops at its end-of-turn tokens.
  • One model that decides and chats on Apple Silicon (#274): scripts/export_mlx.py --unmerged keeps the LoRA out of the weights (adapter/, attached at load by d1a.backends.mlx.attach_lora), and MLXDecisionModel.adapter(False) switches it off per request, giving back the base model exactly. On an instruction-tuned base (D1A-E2B-it) that base is a chat model. D1A_MERGE=0 now attaches a training run's adapter the same way on MLX. The export also carries the base's chat_template.jinja when the run has none. D1A-E2B-it, unmerged 8-bit: 0/42 flips against fp32 on the 28-request bench (max |dp| 0.062, 288 ms per request; the merged build: 0/42, 0.015, 160 ms).
  • scripts/export_mlx.py --q4-0 writes 4-bit weights in groups of 32 on llama.cpp's Q4_0 grid (scale = d, bias = −8·d; d1a.backends.q4_0, numpy only, and d1a.backends.mlx.q4_0_mlx) instead of mlx-lm's min/max scales, for a base trained for Q4_0 such as Gemma 4 12B-it QAT (#270). On that checkpoint Q4_0's weight error is about 20% below mlx-lm's 4-bit group 32 and 30% below group 64. The export's d1a_config.json records "grid": "q4_0".
  • Gemma 4's unified model (the 12B, google/gemma-4-12B-it-qat-q4_0-unquantized) as a base on both backends (#270). PyTorch runs it through transformers as it already ran the E-series. MLX loads it through mlx-lm's Gemma 4 classes (d1a.backends.mlx.unified_classes): the unified checkpoint's vision embedder is dropped on load, and the unified model's zero per-layer-embedding and KV-sharing sizes are stated rather than taken from mlx-lm's E-series defaults. No D1A-12B is trained yet.
  • Pull-request checks adapted from NousResearch/hermes-agent (MIT): blob size (no new file, and no changed binary, over 500 KiB; scripts/check_blob_size.py, exemptions in .github/blob-size-allowlist.txt), shared history (the branch shares history with main) and uv.lock in sync (uv lock --check, uv pinned), plus a weekly OSV-Scanner run on uv.lock (Security tab, detection only) (#269).
  • Served decode rules (rules.<name> in the D1A_LEARNING settings, d1a.learning.rules): for a request that sends the rule's use_case to the run it was fitted for, the served choice of one question becomes the rule's option when the model's choice is one of from and its probability of that option reaches t, and the answer names the rule (rule: name, from, p, t). Only the choice moves: the probabilities, the confidence and the decision log's answers stay the model's, and the log records what was served (meta.served, meta.rules). Written for the live PR labeler: severity's P3/P4 answers are served as P2 when p(P2) >= 0.20, which brought D1A-E4B v0.6 closer to hermes-agent's written label definitions on held-out pull requests (#252). A client that does not send the use case gets exactly the answers it got before.
  • scripts/quality_gate.py --head-learning <file> --labeler-use-case <name> --expect-flips <question>: gates a decode rule; the head server reads the settings file, the labeler replay sends the use case, flips on that question are listed with the probability that crossed the threshold, and every other change still fails.
  • Pull-request hygiene in CI, inside the existing required checks: verified commits refuses AI attribution in commit messages or the pull request body; package builds and installs refuses a pull request without its changes/<PR>-<slug>.md fragment (Dependabot and releases exempt) and any added line ...
Read more

D1A 0.5.0

Choose a tag to compare

@github-actions github-actions released this 11 Oct 07:53
v0.5.0
95d2517

D1A learns from its outcomes on its own: a settings file for the self-learning loop, replay of every earlier outcome onto a new model, a scheduled gate, and learned flags served beside the answer. The 0.3 module paths are gone, and the model server no longer aborts when photo, voice or video requests overlap other work on Apple Silicon.

What's new

  • Self-learning you can configure. D1A_LEARNING=<file> holds the loop's settings (python -m d1a.learning.feedback config init|show|set|validate); feedback tick replays earlier outcomes onto the served model, fail-closed on anything it trained on, and runs the promotion gate after N new outcomes or at a daily time. See docs/CONFIGURATION.md, "Self-learning".
  • Learned flags in answers. A flag fitted offline with python -m d1a.learning.flags and listed in the settings file is served inside its question's answer (flags.<name>), without changing the answer. None is configured by default.
  • A stable server on Apple Silicon. Overlapping media and text requests no longer crash the server (#244): a 45-minute stress run of 756 media and 2,136 text requests ended with no abort, where 0.4.0 aborted within seconds.
  • A smaller package surface. The 27 flat module paths of 0.3 are removed, and new runs are saved without head.pt.

Added

  • recipes/swe-verifier: OpenHands runs (nebius/SWE-rebench-openhands-trajectories, CC-BY-4.0) can be scored, through from_openhands.py and zero_shot.py / self_improve.py --source openhands (#248). Also new: --keep-issue-md (keep the harness's issue.md diff, to measure that shortcut), --exclude (a sample disjoint from an earlier draw) and --issues. The default source, the SWE-agent runs, gives the same rows as before.
  • The server serves learned flags (#237, part 2): a flag in the D1A_LEARNING file's flags, fitted for the served run, is added inside its question's answer (flags.<name>: on, p, t, space), computed on the model's own probabilities before the outcome calibrator, and kept in the decision log's meta. The answer never changes, and the file is re-read when it changes, so a flag is served or withdrawn without a restart.
  • Self-learning v2 (#233, #240):
    • Settings file. D1A_LEARNING=<file> holds the loop's settings, validated, with a last-valid fallback and an audit log. python -m d1a.learning.feedback config init|show|set|validate manages it. Unset, everything behaves as before.
    • Replay. feedback replay and feedback tick re-score an earlier model's decisions that have outcomes through the live server (header x-d1a-replay-of), so a new model starts with every earlier outcome. It is fail-closed: no replay without the served model's complete trained manifest (feedback trained), and none for a decision whose PR or state is in that model's training, or that has no PR id. For E4B v0.6, v0.2 run 1's mix is lost, so its subsets are covered by the whole partitions they came from (night2 dates and unknowable, the hard, devtools and documents train partitions): the exclusion is a superset by partition.
    • The tick runs the gate after N new outcomes or at a daily time. Each gate report gives mde, the smallest log-loss gain its held-out set can detect, and the gate's interval level is a setting.
    • d1a/core/settings.py lists every D1A_* setting with its default and reader, every module reads them through it, and python -m d1a.serving.serve --show-config prints them (secrets masked).

Changed

  • A new run is saved without head.pt (d1a_config.json and head.safetensors only), as 0.4 announced. A run from before 0.4 keeps its head.pt, rewritten with the new metadata (a recalibration, say), so its two copies never disagree; reading head.pt stays.

Removed

  • The flat module paths of D1A 0.3 (d1a.serve, d1a.feedback, d1a.train, and the 24 others; python -m d1a.<old>), shims since 0.4: import and run d1a.serving.serve, d1a.learning.feedback, d1a.training.train, ... instead. d1a/_layout.py keeps the old-to-new map for the provenance scripts. No caller of ours used an old path.

Fixed

  • The model server could abort on Apple Silicon ("commit an already committed command buffer") when photo, voice or video requests overlapped other work (#244): torch's MPS stream is not thread-safe, yet an MLX model's thread synced it on every batch while the media encoder used it from a request thread. With an MLX model the model thread no longer touches torch's MPS stream (MLX waits for its own work), and the encoder copies its result to the CPU under its lock. Text and media still run side by side.

Models tested with this release

Checkpoint Hugging Face tag
D1A-E4B v0.6, PyTorch JohnP1/d1a-e4b@v0.6
D1A-E4B v0.6, MLX 8-bit (with photo, voice and video encoders) JohnP1/d1a-e4b-mlx-q8@v0.6
D1A-E4B v0.6, Core ML (text, ≤ 512 tokens) JohnP1/d1a-e4b-coreml@v0.6
D1A-E2B v0.6, PyTorch JohnP1/d1a-e2b@v0.6
D1A-E2B v0.6, MLX 8-bit (with photo, voice and video encoders) JohnP1/d1a-e2b-mlx-q8@v0.6
D1A-E2B v0.6, Core ML (text, ≤ 512 tokens) JohnP1/d1a-e2b-coreml@v0.6

The Mac mini serves JohnP1/d1a-e4b-mlx-q8@v0.6 with this code (demo smoke test 145/145), and the quality gate on real weights finds no changed answer between 0.4.0 and 0.5.0. Package 0.5.0 serves every model tag from v0.1 to v0.6.

Upgrade notes (from 0.4.0)

  • The 0.3 module paths (d1a.serve, d1a.feedback, d1a.train, ...) are removed: use d1a.serving.serve, d1a.learning.feedback, d1a.training.train, ... (d1a/_layout.py lists every old-to-new name).
  • New runs have no head.pt, so D1A 0.3 cannot load them; 0.4 and later can. Runs that have a head.pt still load.
  • New settings: D1A_LEARNING (the self-learning settings file). With it unset, nothing changes.
  • An answer may carry a flags field when a flag is configured; clients that read answers strictly should allow it.

D1A 0.4.0

Choose a tag to compare

@github-actions github-actions released this 10 Oct 19:41
v0.4.0
7f33fbd

D1A on the v0.6 models: a package layout of its own, release rules with statistical gates, use-case temperatures, Core ML builds for the Apple Neural Engine, one page for every setting, and the first learned decision flags.

What's new

  • v0.6 models. D1A-E4B v0.6 and D1A-E2B v0.6 are the defaults (d1a.core.versions.LATEST). E4B labels current pull requests better than v0.5 (severity 79.1%, change type 90.6% on 487 PRs newer than its training). E2B v0.6 learns every D1A skill at a fraction of the cost (0.20 s per request on a Mac mini M4). Both ship as release exceptions, with the reasons in their model cards.
  • Apple Neural Engine. Both models run through Core ML (JohnP1/d1a-e4b-coreml, JohnP1/d1a-e2b-coreml, text up to 512 tokens, with a reference runner); README "Apple Neural Engine (Core ML)" compares all four builds.
  • Every setting in one place. docs/CONFIGURATION.md lists each D1A_* variable and server flag with its default and when to change it, a test keeps it complete, and CLAUDE.md points coding agents at it.
  • Safer releases and training. scripts/decide.py and scripts/quality_gate.py gate model and serving changes on real weights; a fine-tune refuses a mix that drops a trained skill; calibration refuses data the model trained on; every pull request needs signed (Verified) commits and the CI checks to merge.
  • Use-case temperatures and learned decision flags (d1a.learning.flags: a threshold on a group of options, fitted offline, reported beside the answer; served from 0.5.0).

Added

  • d1a.learning.flags (#237, part 1): learned decision flags. A flag is a group of a choice question's options plus a threshold, raised when the top answer is in the group or the group's probability reaches the threshold; it is read on the model's raw probabilities and never changes the answer. python -m d1a.learning.flags fit sets the threshold at a flag-rate budget on a labelled kit, gate serves it only if it catches more positives than the top answer on a later kit (bootstrap over groups) within the budget. Serving the flag and its settings come with part 2, after #233.
  • A verified commits check fails a pull request when any of its commits lacks GitHub's Verified badge. With the CI tests, lint, the package build and the license check, it is required before anything merges into main (CONTRIBUTING.md, "Pull requests").
  • docs/CONFIGURATION.md: every setting in one place: the server's flags, all 20 D1A_* variables (12 were documented nowhere), the self-learning loop and its commands, photos and voice, agents and evaluation. Each entry gives its default and when to change it. tests/test_configuration_doc.py keeps the page complete, and a CLAUDE.md points coding agents at AGENTS.md and the page.
  • README: an "Apple Neural Engine (Core ML)" section with a table comparing the four D1A builds (E2B and E4B, MLX and Core ML): inputs, length, API, size, speed and answer changes. The Core ML builds are private previews (#224, #231).
  • AGENTS.md: a "Mac resources" section (mandatory). Long jobs on the M1 Max start behind a disk and RAM preflight, fp32 models of 4B or more run only overnight with 25 GB free, a guard pauses D1A jobs on low disk or fast swap growth, and Hugging Face cache entries are deleted only through scan_cache_dir (#228).
  • d1a.serving.serve (#221): a request that sends the header x-d1a-decision-log: off is answered but kept out of the decision log (D1A_FEEDBACK_LOG), on /v1/systemone, /v1/systemone/media, /permute and /separate. The playground's demos and its smoke test send it, so the log's pending decisions are the ones an outcome can still follow.
  • recipes/swe-verifier analysis tools (#220), each run on saved zero_shot.py outputs, CPU only and with no model: recompute_gate.py (the promotion gate between two scored files, with Platt scaling and with D1A's OutcomeCalibrator, out of fold by repository), refit_heuristics.py (the heuristics' regression refitted on dev, on train and on both, scored on the same test rows), token_lengths.py (the token length of every scored input, after checking the rebuilt selection against the saved file run for run) and latency.py (seconds per run). best_of_k.py --pair A:B adds the paired difference between two scorers without moving its other numbers.
  • recipes/swe-verifier/reproduce.sh (#220) downloads the study's scored rows and r1 adapter (JohnP1/d1a-swe-verifier-eval, JohnP1/d1a-swe-verifier-r1; private until the release, REVISION pins them), checks them against their manifest, reruns the scripts that wrote each output and compares every one byte for byte.
  • recipes/mix/build_mix.py, the plan-driven training-mix builder that built D2 and D2-skills (#202), with its plans (recipes/mix/plans/) and a verifier (recipes/mix/verify_mix.py). A plan sets each source's count, stratification and weights, label quotas and an "only unseen" filter. Every eval partition and eval-only kit is screened out (exact state, PR id including the hand-labelled PRs', near-duplicates of the nearest eval state), and the same plan and seed give the same bytes, whatever PYTHONHASHSEED is. A plan that leaves out a training source or decision-v7 is refused unless it names it with a reason, and the sidecar is the one d1a.training.train checks. The verifier checks every record byte for byte against its source and reruns the leak screens.
  • scripts/quality_gate.py --head-run: gates a new checkpoint against the one it replaces (#175).
    • The head server and the head suites load the new weights; the base keeps --run.
    • Changed answers are listed for review instead of failing, since a new checkpoint is meant to change some.
    • Still failures: lower accuracy or worse calibration on a suite, latency above the noise floor, or a request one side does not answer or answers with other options.
  • scripts/compare_checkpoints.py: a training run's verdict table from its benchmark folders (#175).
    • Per suite: each checkpoint's accuracy, and its difference from a reference with a paired 95% bootstrap interval that resamples whole records.
    • ECE at each checkpoint's fitted temperature, from the rows' logits.
    • A pre-registered bar (--bar SUITE=MIN, --floor): exit 1 when a run misses it.
  • recipes/skills/: the skills stage D1A-E4B never had (#175).
    • The mix: every hard-v1 and devtools-v1 training record plus replay, 15,320 records with the trainer's decision-v7 replay.
    • An HF Jobs script. It first runs 5 steps on the 40 longest states to test memory, then trains and calibrates as v0.4 was.
    • The #167 B0 pilot (300 steps) already raised hard-v1 and devtools-v1 by about 5 points each.
  • Fine-tunes keep the skills (#167): recipes/pr-labeler/mix.py --replay-skills N (default 500 per suite) replays the hard-v1, devtools-v1 and documents-v1 train partitions; --replay-skills 0 rebuilds the v0.3 and v0.4 mixes byte for byte. scripts/quality_gate.py --card-suites also scores the five card suites. recipes/README.md and AGENTS.md make both the rule for any run that starts from a trained checkpoint.
  • scripts/quality_gate.py --base <ref> [--head <ref>] (#156): the quality gate for model, loading and serving changes. Two d1a.serve instances, one per version, on the same real weights answer every demo example (regenerated from a d1a-playground checkout with --playground) and the labeler replay, alternating which goes first. A base/base run measures the noise floor. It fails on a changed choice, a probability moving more than --tol (1e-6), a request only one side answers, latency above the floor, or, with --suites, a frozen suite whose accuracy or calibration gets worse; exit 1 on FAIL. AGENTS.md makes it the required step for model and serving PRs and playground pin moves.
  • python -m d1a.feedback promote <log> --calibrator <file> (#148): the promotion gate on a live decision log. Whole groups (an outcome's optional group, e.g. a pull request; POST /v1/feedback takes it) go to the fit or the held-out side. A calibrator fitted on the fit side replaces the served one for a question only if it passes the gate on the held-out side; the file is written whole and renamed into place, so d1a.serve reloads it safely.
  • Self-learning for choice questions (#139). d1a.feedback's OutcomeCalibrator fits one temperature per choice question on the outcomes, which changes how sure an answer is but never which option it picks; gate_choice() applies the promotion rule to the multi-class log loss. Outcomes name their source (POST /v1/feedback's src, or meta["src"]): labels merge per question, and a person's (human) beats another model's (e.g. reviewer) whatever arrived last. python -m d1a.feedback status|records|calibrate --src keeps one source. First stream: the PR labeler's type and blast-radius questions, with the review bot's labels as outcomes.
  • A versioned format for training runs (#64): d1a_config.json (format d1a-torch, version 1: the base, the head's size, the temperature, weights, and the run's recorded arguments as JSON) with the pointer head in head.safetensors, the file names MLX exports already use. d1a.train and d1a.calibrate write it, and loading it unpickles nothing. A run that has both it and a head.pt must say the same in both, or loading stops and names the field. Runs saved before it (head.pt only) load as before, with no end date; published checkpoints are unchanged.
  • Conformance against committed golden vectors (#64): tests/golden/tiny-gemma4/ holds a tiny Gemma 4 checkpoint (weights in git, 556 KB, built by tests/golden/build_tiny_gemma4.py) and golden.json, with 40 requests and 154 questions. tests/test_conformance.py requires its token ids exactly and every probability to 1e-5, through the model, d1a.serve and scripts/golden_vectors.py compare.
  • Training recipes (#63): `python -m d1a.recipe run <recipe.yaml> --out <dir...
Read more

D1A 0.3.0

Choose a tag to compare

@github-actions github-actions released this 05 Oct 11:11
9c93afb

D1A learns from outcomes. Every decision can be logged with the model that made it; when the real outcome comes back (the tests passed, a maintainer corrected a label), D1A recalibrates on it at once and promotes a retrained model only through a statistical gate. Measured first on a coding-agent verifier: three rounds of 300 outcomes cut the calibration error from 0.068 to 0.017, and the gate kept the model each time retraining was not clearly better. This release also makes D1A more its own: Kev's playground, study harness, training-history suites and suite builders are gone (70% of the code is Kev-derived, from 77%), and Gemma 4 runs about a third faster on torch and 20–30% faster on Apple Silicon.

Added

  • recipes/swe-verifier: D1A as a verifier of coding-agent runs (issue, patch and the end of the run → P(the patch resolves the issue)), on public SWE-agent runs (nebius/SWE-agent-trajectories, CC-BY-4.0) held out by repository: zero-shot and LoRA scoring, held-out evaluation with repository-bootstrap intervals and within-issue AUROC, best-of-k, a training-data filter, anytime restarts under a budget, a split-leakage study (random runs vs by issue vs by repository), the self-improvement rounds through d1a.feedback, competition-harness input (from_swegemma.py), verified references, and the paper draft.
  • GET /metrics (d1a.serve): Prometheus text format: whether the model is loaded, requests and batches served, queue depth, recent batch latency (p50, p95, max), prefix-cache hits, misses and states, peak process memory and the model's device memory. Like /v1/models, it does not load an unloaded model.
  • README: the self-learning loop's measured rounds (calibration error 0.068 → 0.017; the gate kept the model when retraining was not clearly better), also on the diagram.
  • Self-learning as a service (d1a.serve): D1A_FEEDBACK_LOG logs every decision and returns a decision_id; POST /v1/feedback records its real outcome; D1A_OUTCOME_CALIBRATOR applies d1a.feedback's calibrator to yes/no answers and re-reads it when the file changes. /v1/models reports the learning state.
  • README: Self-Learning. A section on how D1A learns from outcomes (usage, CLI, the verifier's measured numbers), step 5 in How It Works, and an animated diagram of the loop (docs/arch/learning-{light,dark}.svg from make_svgs.py).
  • d1a.feedback: learning from outcomes. A FeedbackLog records each decision and, later, what actually happened (a patch passed its tests, a label was corrected); OutcomeCalibrator refits each yes/no question on those outcomes (Platt scaling on the logit, which also corrects a shifted base rate that one temperature cannot); records() turns them into training data for a small LoRA update; gate() promotes a candidate only when its held-out log loss is lower with a bootstrap interval clear of zero and no frozen suite regressed. python -m d1a.feedback status | records | calibrate. Fitted values match scikit-learn's logistic regression on D1A-E4B v0.4's verifier outputs.
  • d1a.versions: the newest released checkpoint in one place. latest("JohnP1/d1a-e4b-mlx-q8") gives JohnP1/d1a-e4b-mlx-q8@v0.4; the Space takes its default from it. tests/test_versions.py fails when LATEST and the first row per model of the "Model versions" table below disagree, so a release cannot leave a default behind.
  • A Hugging Face Space, ready to publish (space/, space/push.sh; not hosted yet: a Gradio or ZeroGPU Space needs HF PRO): model routing, guardrails, tool-call gating, the PR labeler (paste a GitHub PR link) and zero-shot photo and voice checks on D1A-E4B v0.4. One d1a.media.MediaModel answers all of them: MediaModel.probs now also scores a text-only record (media None) on the same weights.
  • Kev's eval suites as D1A suites: external (semif-v1, typesafe-v1, wanli-v1, wanli-v2) and night2 (dates, unknowable facts, assertions) are evals/d1a/external and evals/d1a/night2, with the data byte for byte in the private dataset JohnP1/d1a-evals. d1a.benchmark --data evals/d1a/external:semif-v1 scores the same as the Kev suite did (D1A-E4B: identical probabilities on all 252 rows). python -m d1a.suites freeze --provenance records where a suite's data came from.
  • D1A suites (d1a/suites.py, evals/d1a/). Our own datasets, pinned: a manifest per dataset holds its Hub commit and each partition's sha256, record count and role (train or eval), while the text stays in the private dataset. evals/d1a/<suite>:<partition> works wherever --data does (d1a.train, d1a.benchmark). The partition is fetched at the pinned commit and checked before use, and d1a.train refuses an eval partition before loading the model. pr-labels, ja-jglue and routing are frozen. The PR-labeler recipe reads them through recipes/pr-labeler/mix.py, which replaces the job's inline mixing script (byte-identical mixes, round 2's included) and records the inputs' hashes, the parameters and the mix's sha256 beside it. EXTRA now names partitions (train-ja train-blast), and DATA is gone.
  • Video. POST /v1/systemone/media takes {"type": "video"}: an MP4, MOV or WebM clip up to 32 MB, of which 16 frames are sampled evenly and read through Gemma 4's own video path (timestamped frames, the same vision encoder), by the same model on MLX and in d1a.media. The sound track is not used. Zero-shot: on single-scene clips D1A-E4B v0.3 answers as it does for the still photo; questions about the order of events are not reliable yet. New dependency in the media extra: av (PyAV).
  • One model for text, photos and voice. d1a.serve answers POST /v1/systemone/media (a request plus a photo or voice clip) with the model it already serves: Gemma 4's vision and audio encoders turn the media into soft tokens, and the same MLX language model reads them. The encoders (~1 GB for E4B) ship in an export's media/ folder (scripts/export_mlx.py --media, or --media-only for an existing export), and are fetched and loaded on the first photo or voice request. On the playground's 10 samples D1A-E4B v0.3 matches its own PyTorch path (0 of 20 answers change, max |Δp| 0.065). python -m d1a.media stays for PyTorch checkpoints.
  • d1a.serve --idle-unload SECONDS drops the model (and the media encoders) after that long without a request and loads it again on the next one: 2.0 s for D1A-E4B on an M1 Max, which goes from 6.4 GB in use (encoders loaded) to 1.3 GB. GET /v1/models answers without loading the model and reports loaded and idle_unload_s. Default 0 (always loaded), as before.
  • recipes/pr-labeler: how D1A-E4B v0.3 learned to label pull requests, as scripts: fetch labeled PRs from GitHub, build records with the labeling job's own questions, translate them into other languages (on MLX or any OpenAI-compatible server), train and score on one GPU (Hugging Face Jobs or your own), and read the results.

Changed

  • Served PR requests about 20% faster on Apple Silicon (MLX). The question rows ran through all of Gemma 4's layers; its 18 KV-shared layers now run only at the positions the pointer head reads, each as a length-1 row at its own rope offset with an explicit causal (and sliding-window) mask over the shared keys (MLXDecisionModel._picked_hidden). D1A-E4B 8-bit on an M1 Max, 30 PR requests: 2,503 -> 1,955 ms per request (p50; the question rows 1,689 -> 1,013 ms). Exact in fp32 (D1A-E2B, 90 questions: max |dp| 2.3e-6); at 8 bits it moves answers less than running the rows one at a time instead of batched already does (max |dp| 0.015 vs 0.019).

  • Gemma 4 trains and scores about a third faster on torch (the path HF Jobs uses). Its last 18 of 42 layers (E4B; KV-shared) take keys and values from earlier layers, so in the packed forward they now run only over the positions the pointer head reads (each question's <decide> and option ends, about 10-20 per record) instead of the whole document (d1a.backbone.Gemma4.picked_hidden). D1A-E4B v0.4 on 2 PR records (Apple M1 Max, bf16): scoring 4.84 -> 3.00 s, a LoRA training step with checkpointing 12.9-14.0 -> 8.5-8.9 s. Exact in fp32 (D1A-E2B, 120 questions: max |dp| 1.8e-6, gradients equal to 1e-5); in bf16 it moves answers less than changing the batch size does (438 questions: mean |dp| 0.0011 and 2 flips, against 0.0060 and 5 for batch 1 vs 2).

  • Model families behind one interface. d1a.backbone now also decides whether the MLX backend runs a base (Gemma 4 and Qwen3.5; it was a model_type list in d1a.checkpoint) and loads Qwen3.5's CUDA serving kernels (d1a.fused_qwen35, d1a.cuda_graphs) through Qwen35.serve_cuda, so Gemma 4 is the main path and Qwen3.5 a plug-in. d1a.model.is_hybrid and d1a.model.sliding_window are gone: use d1a.backbone.for_config(config). Answers and latency are unchanged.

  • PR-length requests are about 30% faster on Apple Silicon, with identical answers. The state pass (the document, run once before the questions) now stops before Gemma 4's KV-shared layers: those layers keep no cache of their own and nothing reads the state's own outputs, so for the state they were pure waste (18 of D1A-E4B's 42 layers). On an M1 Max, 60 held-out pull requests (median 1,584 state tokens): state pass 3,470 -> 2,137 ms, whole request 4,465 -> 3,082 ms (medians), every probability bit-identical; photos, voice and video too (a 16-frame clip's state pass 1,805 -> 1,079 ms). Other backbones run the full pass as before.

  • Downloading a model from the Hub skips an export's media/ folder (1 GB) until a photo or voice request needs it.

Removed

  • Kev's suite builders (about 6,900 lines, 93-100% Kev's): the hard-v1, breadth-v1, devtools-v1, longdoc-v1, documents-v1/v2 and transfer-v9 builders and generators, build_binding_diagnostic.py, devtools_v1_licences.json, and the tests that only exercised them. The suites stay as frozen data; `tests/test_frozen_su...
Read more

D1A 0.2.0

Choose a tag to compare

@github-actions github-actions released this 02 Oct 14:04
d9b6daf

The first D1A release: Kev's decision model moved onto Gemma 4, with trained checkpoints, an Apple Silicon backend, agent tools, model routing, and photos and voice.

What's new

  • Gemma 4 is the default base. D1A trains and serves on Gemma 4 E2B and E4B. The Qwen bases Kev was built for still work.
  • Better models. D1A-E4B v0.2 (hybrid) adds Kev's later training stages, Japanese decisions and agent routing on top of v0.1: Japanese 0.664 → 0.835, English on new sources 0.680 → 0.704, English on trained sources unchanged (0.848), agent-factory routing 0.933.
  • Fast on a Mac. On Apple Silicon D1A runs on MLX with 8-bit exports that are checked against the PyTorch reference, and Gemma 4's per-layer embeddings are read from disk per request: E2B uses 3.0 GB instead of 4.2 GB.
  • Use it from agents. Ready-made question sets (intake, judge, tier, gate, route) and an MCP server turn each decision into one tool call, and D1A can say whether a prompt needs a small, medium or large model.
  • Use it in your own code. from d1a import D1A loads a checkpoint once and answers questions in-process, with no server.
  • Photos and voice notes. d1a.media answers the same typed questions about an image or a short audio clip, using Gemma 4's own vision and audio encoders (no captioning or speech-to-text). It loads its model on the first request and frees it when idle.

Added

  • MLX backend for Gemma 4 (sliding-window and KV-shared layers), quantized MLX exports (scripts/export_mlx.py) and golden-vector parity checks (scripts/golden_vectors.py) (#39).
  • Gemma 4 per-layer embeddings read from the weight files per request instead of held in memory, with bit-identical results: 1.3 GB less for the 8-bit E2B export, 1.5 GB less for E4B (D1A_PLE_FLASH=0 to decline) (#90).
  • d1a.D1A: load a checkpoint once and answer questions in-process (#47).
  • Agent-factory presets and an MCP server (d1a.mcp_server, extra mcp): d1a_intake, d1a_judge, d1a_tier, d1a_gate, d1a_route, d1a_decide (#52).
  • d1a.media: System One questions about a photo or a voice clip (POST /v1/systemone/media, extra media) (#83), loaded on demand and dropped after --idle-unload seconds (#85).
  • d1a.serve: --device with a kernel probe and a startup self-check of the readout (#43); recent batch latency in /v1/models (#48); a warning when the checkpoint is not calibrated, and calibrated in /v1/models (#54).
  • Training: resume points for LoRA runs (#46); --extra_suites to train on several frozen suites in one run (#67).
  • d1a/backbone.py: what differs between model families in one place, for the torch and MLX paths (#70, #77).
  • scripts/kev_share.py: how much of D1A is still Kev's code, by area (#76).
  • Reports: model routing v0.1 findings, English and Japanese (#56); Apple's Core AI and Foundation Models (macOS and iOS 27) compared with D1A (#86).
  • README: GIFs of the eleven playground demos, and animated architecture diagrams generated by docs/arch/make_svgs.py (#44, #83, #84).
  • CI: Python 3.12 and 3.13, MLX tests on Apple Silicon, a lint pass, a package build and install check, and this release workflow.

Changed

  • The package, modules and settings are D1A's: kev → d1a, KEV_* → D1A_* environment variables, model name d1a-latest (kev-latest and jev-latest still answer) (#1).
  • The default training base is Gemma 4 E2B, and backend="auto" picks MLX for Gemma 4 on Apple Silicon (#1, #39).
  • Model routing sends a prompt to the small tier only at p(small) ≥ 0.7, so close calls go up a tier (#55).
  • The frozen suites are pinned at kev-suites cc4bac8, which adds the hard-v1 and documents-v1 training partitions (#68).

Fixed

  • A proxy in front of d1a.serve (the playground's) could reuse a connection the server had just closed (ECONNRESET): the server now keeps idle connections for 75 s (#53).
  • A GPU whose driver or PyTorch build cannot run a kernel no longer crashes the server on its first request: the device is probed at startup and the server falls back to the CPU (#43).
  • A non-finite loss or gradient skips its batch instead of ending the run; three in a row still end it, because then the weights themselves are broken (#46).

Removed

  • Kev's research tooling, deployments and records that D1A does not use: 26 files, about 5,000 lines (#69).

Models tested with this release

Checkpoint Hugging Face tag
D1A-E4B v0.2 (hybrid), PyTorch JohnP1/d1a-e4b@v0.2-hybrid
D1A-E4B v0.2 (hybrid), MLX 8-bit JohnP1/d1a-e4b-mlx-q8@v0.2-hybrid
D1A-E2B v0.2.1 (2 epochs, calibrated), PyTorch JohnP1/d1a-e2b@v0.2.1-2epoch-calibrated
D1A-E2B v0.2, MLX 8-bit JohnP1/d1a-e2b-mlx-q8

Upgrade notes (from Kev)

  • Rename imports and commands: python -m kev.serve → python -m d1a.serve, from kev... → from d1a..., and KEV_* environment variables → D1A_*.
  • On Apple Silicon the server now uses MLX for Gemma 4 by default; set D1A_BACKEND=torch for PyTorch MPS.
  • Optional features are extras: serve (HTTP server), mlx (Apple Silicon), media (photos and voice), mcp (agent tools).