Skip to main content

Moonshiner

Moonshiner authors seeds, creates model traces, judges every trace, retries rejected traces, prepares training data, and publishes accepted trajectories to Hugging Face.

Install

With pip:

python3 -m pip install moonshiner

With the installer:

curl -fsSL https://raw.githubusercontent.com/greghavens/moonshiner/main/install.sh | bash

Quick start

After installation, run:

moonshiner

That is the complete quick start. On the first run, Moonshiner confirms where to keep the project's configuration and data, asks for your preferences and anything else it needs, saves the configuration, and starts the pipeline.

Moonshiner asks:

  • which harness and model should create traces;
  • which harness and model should judge traces;
  • which harnesses and models should author and judge seeds, when seed authoring is enabled;
  • which provider Pi should use;
  • the provider credential, when that provider requires one;
  • the Hugging Face dataset target, when publishing is enabled.
  • the Hugging Face publication format when changing advanced setup choices.

Trace and seed sources may use Pi or Codex. Claude Code is supported only as a judge because its native trace output does not expose readable reasoning text. Codex and Claude Code can use their existing login sessions. Pi is a harness, so Moonshiner asks which provider Pi should call and configures the matching endpoint, protocol, model, and credential.

After setup, moonshiner starts every enabled queue for that project. Seed authoring, tracing, judging, retries, formatting, privacy checks, local append-only storage, and Hugging Face publication continue independently. Active model calls are allowed to finish.

Check progress

moonshiner status

Status reports active workers, authored seeds, accepted and rejected traces, retraces, rates, session progress, and publishing progress.

Check the installation and project configuration:

moonshiner doctor

Configure

Run the setup assistant again:

moonshiner setup

Show the current project's configuration:

moonshiner config show

Configure a role directly:

moonshiner config role trace-author pi PROVIDER/MODEL REASONING
moonshiner config role trace-judge codex gpt-5.6-sol xhigh
moonshiner config role seed-author codex gpt-5.6-sol xhigh
moonshiner config role seed-judge codex gpt-5.6-sol xhigh
moonshiner config role trace-author vllm ORGANIZATION/MODEL

Save a provider credential:

moonshiner auth set openrouter

Moonshiner stores credentials outside the project directory and removes credentials, user keys, email addresses, and host-identifying paths from data before publication.

Project storage

Each working directory is a separate Moonshiner project. Configuration and output default to .moonshiner/ in the current directory.

moonshiner storage status

To use another location, change to that directory and run moonshiner. Moonshiner asks you to confirm the directory before creating the project configuration.

Seeds

Browse the seed catalog:

moonshiner seeds catalog

Search the catalog by category, name, or training tag:

moonshiner seeds catalog --category parallel-same
moonshiner seeds catalog --name calendar
moonshiner seeds catalog --tag execution:parallel

Author and review one seed:

moonshiner seed run --id calendar-reschedule --brief "Reschedule independent appointments while preserving all constraints" --yes

Completed seeds enter the trace queue automatically. Categories and tags organize the catalog and can optionally select training data; they do not create separate trace pipelines.

Seed verifiers may declare executable and PowerShell-module prerequisites in task.json. Moonshiner resolves a host PowerShell 7 installation—including a user-local pwsh on Linux—or installs Homebrew's powershell formula when it is absent. Declared PowerShell modules are downloaded with their exact declared versions and dependencies into project-managed storage, then mounted read-only inside the offline verifier sandbox. A provisioning failure stops seed authoring before the seed judge is called and preserves the authored candidate.

Traces

Running moonshiner is the normal way to keep tracing continuously. To request a deliberate bounded run:

moonshiner run --limit 20 --yes

Trace workers process separate seeds concurrently. Each seed remains an independent work item: create one trace, judge it, retry it after a judge rejection when attempts remain, then format, scrub, and queue an accepted trace for publication.

Set trace concurrency:

moonshiner config set pipeline.trace.workers 3

Set the maximum attempts for each individual trace:

moonshiner config set pipeline.trace.max_attempts 3

Trace retries step down reasoning effort by default: xhigh, then medium, then low. Each later attempt runs only after the preceding attempt is rejected, and the first judge-accepted trace is retained. Attempt counts above three repeat that cycle. To keep the teacher's configured reasoning effort unchanged across all trace attempts:

moonshiner config set pipeline.trace.step_down_reasoning_on_failure false

For catalogued coding programs, Moonshiner appends its coding guidance to Pi's native system prompt by default. It does not replace Pi's native prompt, disable Pi skills or project context, restrict Pi's native tools, or alter authored seed turns.

Set Hugging Face publication batch size:

moonshiner config set publish.batch_size 10

Choose the Hugging Face publication format:

moonshiner config set publish.format parquet-shards
moonshiner config set publish.include_jsonl false

Supported values are jsonl, jsonl-hf-parquet, and parquet-shards. All three publish the same validated canonical rows. parquet-shards publishes both the canonical JSONL and compressed active shards, points the viewer only at the shards, and commits the JSONL, shards, manifest, and updated dataset card together. jsonl-hf-parquet publishes JSONL for Hugging Face to convert, while jsonl keeps JSONL as the configured source format. With parquet-shards, set publish.include_jsonl to false to publish only the card, manifest, banner, and active Parquet shards while retaining the canonical JSONL locally.

These settings are reread between work items. Reducing concurrency does not cancel active model calls.

Hugging Face keeps old LFS objects in repository history. To preview an occasional history cleanup while retaining the newest 10 complete, distinct dataset snapshots:

moonshiner maintenance prune-hf-history --keep 10

The preview lists every retained snapshot and the exact amount eligible for deletion. After reviewing it, run the permanent history rewrite explicitly:

moonshiner maintenance prune-hf-history --keep 10 --yes

This maintenance action is never automatic. It refuses incomplete snapshots, preserves LFS objects reachable from tags, pull requests, conversion refs, and branches besides main, and lets an active publish finish before rewriting history.

Local models and token distributions

The vllm backend traces against any OpenAI-compatible chat-completions server; a local vLLM is the reason it exists. It runs its own agent loop with bash, file read, write, and edit, and directory listing, inside the same workspace sandbox every other backend's tools run in.

moonshiner config role trace-author vllm ORGANIZATION/MODEL
moonshiner config set runtimes.vllm.base_url http://127.0.0.1:8000/v1

A local server needs no credential. For one that does, set runtimes.vllm.requires_key to true and save the key named by runtimes.vllm.key_env. Sampling comes from runtimes.vllm.sampling (temperature, top_p, max_tokens, and the usual neighbors). Judging can stay on any other backend.

Capture token distributions

Self-distillation trains a student against the teacher's distribution over tokens rather than against the tokens the teacher happened to sample, so it needs that distribution recorded. Capture is off by default:

moonshiner config set runtimes.vllm.logprobs.enabled true
moonshiner config set runtimes.vllm.logprobs.top_k 100

vLLM caps top_logprobs server-side, at 20 unless told otherwise, so start the server with a cap at least as large as the configured top_k:

vllm serve ORGANIZATION/MODEL --max-logprobs 100

A server that refuses the requested K fails the attempt and names both the flag and the setting. Moonshiner never falls back to fewer alternatives: a trajectory captured at a smaller K than the run asked for trains without complaint against a different target.

Distributions are written beside the trace, one Parquet sidecar per trajectory, never inlined into the trace JSON. At K=100 a 60K-token trajectory is roughly 24 MB of alternatives. Each generated token is one row:

Column Meaning
trajectory_id, assistant_turn_index, token_index The alignment key. assistant_turn_index counts assistant messages from 1; token_index runs 0..n-1 within that turn
token_id, logprob The token the server sampled, and its logprob
top_token_ids, top_logprobs The K alternatives, as returned
segment Always assistant_generated
top_k_requested, prompt_token_count, finish_reason, model What produced the turn

Only assistant-generated tokens have rows. Prompt and tool-result tokens are not generated by the model, carry no distribution, and appear nowhere in the sidecar; segment states that boundary on every row rather than leaving it to be inferred from an absence. Tokens are the server's own integer ids, never decoded strings: a tokenizer round trip that disagreed by a single token would shift every target against its distribution from that point on, and the loss curve would look fine. The top-K is stored exactly as returned, with no renormalization, so the mass outside the captured head remains visible to a KL objective.

Sidecar rows join to dataset rows on keys a published dataset already carries: source_trajectory_id and assistant_step are the sidecar's trajectory_id and assistant_turn_index. Publication copies the sidecars to logprobs/ beside the rows and writes logprobs/MANIFEST.json with those alignment keys, per-trajectory hashes, token counts, and sizes. moonshiner dataset analyze counts sidecar bytes once per trajectory in its storage totals, whether it reads the local rows or a published dataset's manifest.

Keep the teacher's own distribution

Publishing only judge-accepted traces, and stepping reasoning effort down after a rejection, both move the recorded distribution away from the model being reproduced. For self-distillation, turn both off:

moonshiner config set pipeline.trace.skip_judging true
moonshiner config set pipeline.trace.step_down_reasoning_on_failure false

Both keep Moonshiner's usual behavior by default. An unjudged trace is accepted and says so: its review records judge.bypassed, its screening is unjudged-distillation-v1, and its verifier drops independent-review, so no unjudged row can be mistaken for a judged one.

Synthetic corrections companion

Synthetic corrections are optional and disabled by default. They are intended only for traces that exhausted their normal trace attempts, never passed the trace judge, and missed an otherwise correct result by a small, obvious defect. Examples include an omitted tool call, a one-line code fix, or a genuinely missing small file. Rewritten reasoning, refactoring, broad replanning, invented work, and unrelated changes are rejected.

Configure the feature:

moonshiner synthetic-corrections configure

The correction harness and model default to the current trace judge. Moonshiner asks for another provider credential only if the selected runtime needs one and no credential is already configured. The companion Hugging Face target defaults to the primary dataset name with -synthetic-corrections appended. Each trace gets at most two correction attempts by default.

Preview eligible work without model calls:

moonshiner synthetic-corrections run --dry-run

Start paid correction processing explicitly:

moonshiner synthetic-corrections run --yes

Moonshiner examines up to three preserved failures for each use case but creates at most one corrected trajectory. The eligibility reviewer chooses the correctable failure requiring the smallest valid change; correction retries continue from that same preserved attempt. A use case is excluded if any current-revision trace ever passed. The source reasoning must remain unchanged, the correction must be minimal, and the corrected trace must pass normal verification and the normal independent trace judge. A judge rejection returns the item to the end of the correction queue with feedback while attempts remain.

Corrections use the existing judge path and publication queue. Accepted corrections are routed explicitly to an isolated companion dataset with the same canonical schema, publication format, generated card, and banner as the primary dataset. They never enter or modify the primary dataset.

Show correction status:

moonshiner synthetic-corrections status

Resume existing work

Import an existing directory of traces or prepared rows:

moonshiner trace import --directory /path/to/existing-data

Import an existing Hugging Face dataset:

moonshiner trace import --hf owner/dataset --revision COMMIT

Convert imported prepared rows to Moonshiner's current canonical schema, then publish them with the project's configured JSONL or Parquet-shard format:

moonshiner migrate-dataset --yes
moonshiner publish --yes

Migration preserves existing messages and tools. It does not generate or infer missing trace content such as model reasoning.

For the configured Hugging Face target, Moonshiner downloads the remote trace file only when the matching local file does not yet exist. Later accepted traces append to the local canonical file. Existing rows are never replaced.

Enable a remote revision check before each append when you need it:

moonshiner config set publish.check_before_append true

Build and prepare datasets

Build the accepted local traces into a validated dataset:

moonshiner dataset build

Analyze one or more local or revision-pinned Hugging Face datasets before combining them:

moonshiner dataset analyze --source local:/data/private.jsonl --source hf:HuggingFaceH4/ultrachat_200k@COMMIT#train_sft

With no --source, Moonshiner analyzes the configured local append-only dataset. A direct Hugging Face file also works without installing dataset tooling:

moonshiner dataset analyze --source hf-file:owner/dataset@REVISION/path/to/traces.jsonl

You can also paste the file's Hugging Face URL directly:

moonshiner dataset analyze --source https://huggingface.co/datasets/owner/dataset/blob/REVISION/path/to/traces.jsonl

The report compares trajectories, rows, target tokens, total tokens, length distributions, categories, tags, sources, multi-turn conversations, direct responses, sequential tool calls, and parallel tool calls. Add --tokenizer organization/model for exact tokenizer counts; otherwise Moonshiner clearly labels its token estimate.

Combine local data with revision-pinned Hugging Face datasets:

moonshiner dataset compose --source local:/data/private.jsonl --source hf:HuggingFaceH4/ultrachat_200k@COMMIT#train_sft --out /data/prepared/train.jsonl

Preview a token-budgeted composition without writing it:

moonshiner dataset compose --source local:/data/private.jsonl --source hf:owner/dataset@COMMIT#train --target-tokens 50000000 --weight-category 'tool-calling=2' --weight-tag 'execution:parallel=3' --tokenizer organization/model --dry-run

Remove --dry-run to write the composition after reviewing the reported realized mix. Weight rules use GLOB=WEIGHT; category, tag, and source-pattern weights require --target-tokens. --weight-unit selects whether sampling balances rows, target tokens, or total tokens. No curriculum percentage is hard-coded.

Select rows by name, category, or training tag:

moonshiner dataset compose --source local:/data/all.jsonl --include-category 'tool-*' --include-tag parallel-tool-calls --exclude-tag sensitive --out /data/prepared/selected.jsonl

Review training risks:

moonshiner dataset readiness --source local:/data/prepared/train.jsonl --tokenizer organization/model --context-length 32768

Readiness checks context truncation, empty final answers, duplicate prompts, malformed tool sequences, repetitive reasoning, mixed-language scripts, privacy findings, cumulative trajectory prefixes, and small category shares. Analysis and readiness report privacy finding types and counts without printing the affected row contents. They are advisory only; composition and publication remain fail-closed for privacy findings.

Prepare trainer configuration:

moonshiner dataset prepare --trainer axolotl --input /data/prepared/train.jsonl --model organization/model --tokenizer organization/model --sequence-len 32768 --out /data/prepared/axolotl.json

Packing is off unless --sample-packing is supplied. Moonshiner normalizes mixed conversation formats, scrubs private data, deduplicates rows, and writes reproducible composition and trainer manifests. Manifests include pinned Hugging Face revisions or local file hashes, tokenizer accounting, filters, requested and realized mixtures, input and output hashes, trainer configuration, package versions, and the exact trainer command.

Accepted traces also receive observed tags such as response:direct, reasoning:planning, reasoning:extended, reasoning:self-correction, reasoning:verification, interaction:multi-turn, execution:parallel, and format:strict-json. These describe what the trace actually demonstrated; they are catalog and composition metadata, never queue partitions or acceptance gates.

Hugging Face publishing

Set the target dataset:

moonshiner config set publish.hf_dataset owner/dataset

Log in with the Hugging Face CLI or save the token through Moonshiner. Accepted trajectories are appended locally and published in configured batches. Dataset-card counts and percentages regenerate from the exact published rows.

Parquet publication keeps dataset-manifest.json as the authoritative active shard list. Replaced trajectories supersede their prior active shard without losing neighboring trajectories; superseded shard content remains recoverable from repository history. Every publish commit contains the data artifacts, manifest, and matching card together.

Manual publication is available for repair or verification:

moonshiner publish --yes

Seed library

Application releases and seed-catalog releases are versioned separately, so the catalog can grow without requiring an application update.

moonshiner seeds status
moonshiner seeds update
moonshiner seeds verify

Seed identifiers are immutable. Moonshiner never overwrites or removes an existing seed during normal operation.

Agent use

The repository includes the skills/moonshiner-runner skill for agents operating Moonshiner. Agents should use the same installed moonshiner commands, project configuration, status output, and released application as human users.

License

Moonshiner is licensed under the Apache License 2.0.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

moonshiner-0.8.8.tar.gz (32.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

moonshiner-0.8.8-py3-none-any.whl (41.9 MB view details)

Uploaded Python 3

File details

Details for the file moonshiner-0.8.8.tar.gz.

File metadata

  • Download URL: moonshiner-0.8.8.tar.gz
  • Upload date:
  • Size: 32.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for moonshiner-0.8.8.tar.gz
Algorithm Hash digest
SHA256 1fddfcc2a390a30a55a07569a82acb1dcf34ae775f0ec5cbc5eac9c9083979c9
MD5 df2888fc587b4ce369195f4e7da02398
BLAKE2b-256 ae8ad7c75412238e84cecb3cc2ec2afb46183dcfdc9674a96fabac75347eeeed

See more details on using hashes here.

Provenance

The following attestation bundles were made for moonshiner-0.8.8.tar.gz:

Publisher: release.yml on greghavens/moonshiner

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file moonshiner-0.8.8-py3-none-any.whl.

File metadata

  • Download URL: moonshiner-0.8.8-py3-none-any.whl
  • Upload date:
  • Size: 41.9 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for moonshiner-0.8.8-py3-none-any.whl
Algorithm Hash digest
SHA256 c6609aa7f643deb67a745c74cec1a88d9a584ab58f98a978b04b9d4de94f716b
MD5 6e1eb65d31d08c9f9525e1f6ab626dc3
BLAKE2b-256 cd531d5fd381789105c8cfad72e1061ca95defeb4dbc107a37863deea26059eb

See more details on using hashes here.

Provenance

The following attestation bundles were made for moonshiner-0.8.8-py3-none-any.whl:

Publisher: release.yml on greghavens/moonshiner

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.8.23

2 files

0.8.22

2 files

0.8.21

2 files

0.8.20

2 files

0.8.19

2 files

0.8.18

2 files

0.8.17

2 files

0.8.16

2 files

0.8.15

2 files

0.8.14

2 files

0.8.13

2 files

0.8.12

2 files

0.8.11

2 files

0.8.10

2 files

This release

0.8.8 This release

2 files

0.8.7

2 files

0.8.6

2 files

0.8.5

2 files

0.8.4

2 files

0.8.3

2 files

0.8.2

2 files

0.8.1

2 files

0.8.0

2 files

0.7.65

2 files

0.7.64

2 files

0.7.63

2 files

0.7.62

2 files

0.7.61

2 files

0.7.60

2 files

0.7.59

2 files

0.7.58

2 files

0.7.57

2 files

0.7.56

2 files

0.7.55

2 files

0.7.54

2 files

0.7.53

2 files

0.7.52

2 files

0.7.51

2 files

0.7.50

2 files

0.7.49

2 files

0.7.48

2 files

0.7.47

2 files

0.7.45

2 files

0.7.43

2 files

0.7.42

2 files

0.7.41

2 files

0.7.40

2 files

0.7.39

2 files

0.7.37

2 files

0.7.36

2 files

0.7.35

2 files

0.7.34

2 files

0.7.33

2 files

0.7.32

2 files

0.7.31

2 files

0.7.30

2 files

0.7.29

2 files

0.7.28

2 files

0.7.27

2 files

0.7.26

2 files

0.7.25

2 files

0.7.24

2 files

0.7.23

2 files

0.7.22

2 files

0.7.21

2 files

0.7.20

2 files

0.7.19

2 files

0.7.18

2 files

0.7.17

2 files

0.7.16

2 files

0.7.15

2 files

0.7.14

2 files

0.7.13

2 files

0.7.12

2 files

0.7.11

2 files

0.7.10

2 files

0.7.9

2 files

0.7.8

2 files

0.7.7

2 files

0.7.6

2 files

0.7.5

2 files

0.7.4

2 files

0.7.3

2 files

0.7.2

2 files

0.7.1

2 files

0.7.0

2 files

0.6.9

2 files

0.6.8

2 files

0.6.7

2 files

0.6.6

2 files

0.6.5

2 files

0.6.4

2 files

0.6.3

2 files

0.6.2

2 files

0.6.1

2 files

0.5.67

2 files

0.5.66

2 files

0.5.65

2 files

0.5.64

2 files

0.5.62

2 files

0.5.61

2 files

0.5.60

2 files

0.5.59

2 files

0.5.58

2 files

0.5.57

2 files

0.5.56

2 files

0.5.55

2 files

0.5.54

2 files

0.5.53

2 files

0.5.51

2 files

0.5.50

2 files

0.5.49

2 files

0.5.48

2 files

0.5.46

2 files

0.5.45

2 files

0.5.44

2 files

0.5.43

2 files

0.5.42

2 files

0.5.41

2 files

0.5.40

2 files

0.5.39

2 files

0.5.38

2 files

0.5.37

2 files

0.5.36

2 files

0.5.35

2 files

0.5.34

2 files

0.5.33

2 files

0.5.32

2 files

0.5.31

2 files

0.5.30

2 files

0.5.29

2 files

0.5.28

2 files

0.5.27

2 files

0.5.26

2 files

0.5.25

2 files

0.5.24

2 files

0.5.23

2 files

0.5.22

2 files

0.5.21

2 files

0.5.20

2 files

0.5.19

2 files

0.5.18

2 files

0.5.17

2 files

0.5.16

2 files

0.5.15

2 files

0.5.14

2 files

0.5.13

2 files

0.5.12

2 files

0.5.11

2 files

0.5.10

2 files

0.5.8

2 files

0.5.7

2 files

0.5.6

2 files

0.5.5

2 files

0.5.4

2 files

0.5.3

2 files

0.5.2

2 files

0.5.1

2 files

0.5.0

2 files

0.4.80

2 files

0.4.79

2 files

0.4.78

2 files

0.4.77

2 files

0.4.76

2 files

0.4.74

2 files

0.4.73

2 files

0.4.72

2 files

0.4.71

2 files

0.4.70

2 files

0.4.69

2 files

0.4.68

2 files

0.4.67

2 files

0.4.66

2 files

0.4.65

2 files

0.4.64

2 files

0.4.63

2 files

0.4.62

2 files

0.4.60

2 files

0.4.59

2 files

0.4.58

2 files

0.4.57

2 files

0.4.56

2 files

0.4.55

2 files

0.4.54

2 files

0.4.53

2 files

0.4.51

2 files

0.4.50

2 files

0.4.49

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page