v0.20.0
Highlights
chapkit migrate — convert an MLproject into a committable chapkit service (#39)
Point chapkit at an MLflow-style MLproject directory and get a first-class chapkit project you can edit, commit, extend, and ship as your own image. Code-generating sibling of chapkit run — where run adapts at runtime without touching files, migrate writes a proper project layout.
chapkit migrate # current directory
chapkit migrate --dry-run # print the plan, touch nothing
chapkit migrate --yes # skip prompts (CI)Generates at the project root: main.py, Dockerfile (pointing at the right chapkit-images base), pyproject.toml + requirements.txt with your original deps merged in, compose.yml, Makefile, postman_collection.json, CHAPKIT.md, plus .gitignore / .dockerignore. Original MLproject, stale data, and anything chapkit regenerates go to _old/ (recoverable — never deleted).
Key behaviours:
- Dependency merge from three sources (user
pyproject.toml,requirements.txt, MLprojectpython_env/conda_env— falling back topyenv.yaml/conda.yaml/environment.yaml).sklearn->scikit-learnrewrite. Conda-native scalars filtered out. PEP 508 markers with quotes,-rincludes (recursive),-cconstraints,--find-links,--extra-index-url,-e .all pass through;[tool.uv]gets index / find-links / constraint-dependencies for uv-first workflows. user_optionsbecome typedBaseConfigfields. Non-identifier names (n-lags,class,1st_period) normalized on the Python class and preserved on the wire via PydanticField(alias="...").- Base image auto-detection:
chapkit-py/chapkit-r/chapkit-r-inlafrom source content + MLprojectdocker_env. Override with--base-image. - Interactive prompts for plan confirmation, ambiguous mixed-language base-image, unknown MLproject parameters;
--yessuppresses for CI,--dry-runnever prompts. _old/is never overwritten: hard error if it already exists. No--force, no timestamp rotation.
Validated end-to-end against six real chap-models repos (3/6 fully green out of the box; the remaining three migrate cleanly and services come up healthy — their failures are upstream model bugs with documented one-line patches).
ShellModelRunner.config_format — match chap-core's config.yml layout (#39)
New config_format: Literal["flat", "chap_core"] arg on ShellModelRunner. migrate-generated main.py opts into "chap_core" so config.yml matches chap-core's ModelConfiguration shape — reserved fields (prediction_periods, additional_continuous_covariates) stay top-level, everything else nests under user_option_values. Scripts ported from chap-models repos can read config["user_option_values"]["<option>"] (Python) or config$user_option_values$<option> (R) unchanged. chapkit init scaffolds keep the "flat" default — no behaviour change for existing users. chapkit run's runtime path picks up the same chap-core format.
Guards + fixes that also benefit chapkit run
runner.py'sValidationDiagnosticimport is guarded behindTYPE_CHECKING, so volume-mounting currentrunner.pyonto a container whose chapkit pre-dates that class no longer breaks module load.chapkit runinherits the same sanitization: non-identifieruser_options,dump_config_yamlviaby_alias=True, canonical parameter mapping.- Startup prints a Docker one-liner hint to short-cut the typical "which image do I mount?" question.
Docs
chapkit migratereference — flags, classification rules, generated files, base-image auto-detection,user_options-> Config, dry-run, interactive prompts.- MLproject migration checklist — before / during / after migration for shell-based runners, including the three failure shapes seen in the chap-models sweep.
- Generated
CHAPKIT.mdin each migrated repo — TL;DR table, run-locally vs Docker, expandedchapkit testsection,config.ymllayout, covariates guidance.
Full changelog
- feat(cli): add chapkit migrate for MLproject -> chapkit conversion (#39)
- feat(runner,migrate): opt-in chap-core config.yml layout via config_format
- feat(run,mlproject): carry the migrate fixes into chapkit run's runtime path
- feat(run): print Docker one-liner hint on startup
- feat(migrate): detect install_packages.R and bake its packages into the image
- feat(migrate): warn about unpinned dependencies in the summary output
- feat(migrate): fall back to conventional env files when MLproject is silent
- feat(migrate): emit MLServiceInfo with model_metadata pulled from MLproject
- feat(migrate): generate a Makefile with install/run/test/docker targets
- feat(migrate): wire .with_registration() into generated main.py
- feat(migrate,test): pyenv/conda dep merge, sklearn alias, chap-core-standard columns
- feat(test): --predict-rows and new default for 2y+ context models
- fix(runner): guard ValidationDiagnostic import behind TYPE_CHECKING
- fix(migrate): guard against main.py collisions (chap-models/chap_pymc case)
- fix(migrate): follow -r recursively, preserve index directives, warn on unknown
- fix(migrate): rewrite nested -c constraint and --find-links paths relative to project root
- fix(migrate): -c constraint files pass through verbatim, don't recurse as deps
- fix(migrate): read deps from requirements.txt too; install via
uv pip install -r, not inline shell args - fix(migrate): escape templated strings, filter conda-native deps, alias non-identifier option names
- fix(migrate): follow pip's comment rules when parsing requirements.txt
- fix(migrate): pin chapkit>=0.19.0 floor; USER root in Dockerfile
- fix(migrate,init): image-level healthcheck; drop redundant compose declaration
- chore: release v0.20.0
Full diff: v0.19.0...v0.20.0