[project] name = "document-language-model" version = "0.10.0" description = "Directive-driven local LLM training, retraining, and export from .dlm documents, codebases, and multimodal sources." readme = "README.md" requires-python = ">=3.11" license = { text = "MIT" } authors = [{ name = "espadonne", email = "mfwolffe@outlook.com" }] keywords = ["llm", "lora", "fine-tuning", "ollama", "local-ai"] classifiers = [ "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Topic :: Scientific/Engineering :: Artificial Intelligence", ] dependencies = [ # CLI / doc / lightweight plumbing. "typer>=0.12", "rich>=13.7", "prompt-toolkit>=3.0", # dlm repl interactive prompt (Sprint 24) "watchfiles>=0.24", # dlm train --watch save-to-train loop (Sprint 25) "python-ulid>=3.0", # dlm_id generation (Sprint 13) "pydantic>=2.9", # schema validation (Sprint 03+) "pyyaml>=6.0", # .dlm frontmatter (Sprint 03+) "psutil>=6.0", # hardware doctor (Sprint 05) "zstandard>=0.23", # replay corpus framing (Sprint 08) "cbor2>=5.6", # replay snapshot encoding (Sprint 08) "packaging>=24.0", # lock policy semver parsing (Sprint 15) # ML runtime. `dlm train`, `dlm prompt`, `dlm export` all import these; # a `pip install dlm` that omits them would ImportError on first call # (audit-05 B1). "torch>=2.4", "transformers>=4.45", "peft>=0.13", "trl>=0.12", "datasets>=3.0", "huggingface-hub>=0.25", "accelerate>=1.0", # dlm train --gpus launcher (Sprint 23; audit-08 M2) "safetensors>=0.4", # GGUF conversion + adapter I/O (Sprint 11) "sentencepiece>=0.2", # vendored llama.cpp convert_lora_to_gguf imports it (audit-08 P3) ] [project.optional-dependencies] # CUDA-only. `bitsandbytes` won't install on macOS / CPU-only boxes; gate # behind an extra so `pip install dlm` stays portable and `pip install # dlm[cuda]` unlocks QLoRA. cuda = [ "bitsandbytes>=0.43", ] # Apple Silicon only (Sprint 21). `mlx` + `mlx-lm` wheels are darwin-arm64 # exclusives; env markers keep `uv sync --extra mlx` a no-op on non-Apple # hosts so wheel resolution doesn't fail for Linux/CUDA contributors. mlx = [ "mlx>=0.18; sys_platform == 'darwin' and platform_machine == 'arm64'", "mlx-lm>=0.19; sys_platform == 'darwin' and platform_machine == 'arm64'", ] # Sprint 26 observability sinks. TensorBoard + W&B are optional — core # metrics (SQLite) work without them. `uv sync --extra observability` # unlocks `dlm train --tensorboard` and `dlm train --wandb `. observability = [ "tensorboard>=2.15", "wandb>=0.17", ] # Sprint 35.2 audio-language bases. `soundfile` is the only dep needed # to decode .wav / .flac / .ogg; MP3 support would require libsndfile # ≥1.1 and is deferred. Keep optional so text-only users don't pull # the libsndfile C library. `soxr` is the polyphase resampler used by # `training.audio.auto_resample=True`; without it (or scipy as a # fallback) the resample path raises `AudioResampleUnavailable` # rather than training on the wrong sample rate. audio = [ "soundfile>=0.12", "soxr>=0.3", ] # Sprint 43 synth teachers. API clients are optional — `dlm synth` # works with `self` and `hf:` teachers without these. Install via # `pip install dlm[openai]` or `pip install dlm[anthropic]`. openai = [ "openai>=1.0", ] anthropic = [ "anthropic>=0.30", ] # Sprint 26 (X1) cross-repo bridge: `dlm export --emit-sway-json` calls # into ``dlm_sway.integrations.dlm.autogen`` to write a ready-to-run # sway.yaml alongside the GGUF. NB: pulls plain `dlm-sway`, NOT # `dlm-sway[dlm]` — `dlm-sway[dlm]` would round-trip back to this # package and create a pip resolver cycle. Plain `dlm-sway` is enough # because `build_spec_dict` operates on data structures the dlm # bridge already exposes from sway's side. sway = [ "dlm-sway>=0.1.0", ] [project.scripts] dlm = "dlm.cli.app:main" [project.urls] Homepage = "https://github.com/tenseleyFlow/DocumentLanguageModel" Issues = "https://github.com/tenseleyFlow/DocumentLanguageModel/issues" [dependency-groups] dev = [ # Test + lint tooling only. ML runtime moved to [project].dependencies # (audit-05 B1) so `pip install dlm` gives users a working CLI. "pytest>=8.0", "pytest-cov>=5.0", "mypy>=1.11", "ruff>=0.6", "types-pyyaml>=6.0", "types-psutil>=6.0", "hypothesis>=6.152.1", ] docs = [ # Sprint 16: MkDocs Material site. Separated from `dev` so contributors # working on code don't pay the docs dep surface. "mkdocs>=1.6", "mkdocs-material>=9.5", ] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["src/dlm"] # -------- ruff -------- [tool.ruff] line-length = 100 target-version = "py311" src = ["src", "tests"] # Vendored third-party code (llama.cpp) — don't lint or format it. extend-exclude = ["vendor/llama.cpp"] [tool.ruff.lint] select = [ "E", # pycodestyle errors "F", # pyflakes "W", # pycodestyle warnings "I", # isort "UP", # pyupgrade "B", # bugbear "N", # pep8-naming "C4", # comprehensions "SIM", # simplify "PT", # pytest "RET", # return "ARG", # unused args "PTH", # use pathlib "TID", # tidy imports ] ignore = [ "E501", # handled by formatter ] [tool.ruff.lint.per-file-ignores] # SIM117 (combinable `with`) is noisy in tests that stream tar+zstd # into a sequence of readers; the nested form is clearer than the # comma-separated one. "tests/**/*.py" = ["ARG", "PT011", "SIM117"] # Typer stub subcommands accept every CLI arg the real implementation # will take so `--help` reflects the shipping surface — even though the # stub body discards them. "src/dlm/cli/commands.py" = ["ARG001"] # HuggingFace Trainer callbacks MUST accept `args`/`state`/`control` # positionally even when the implementation only reads some of them — # HF dispatches them by position. ARG002 for these wrappers is noise. "src/dlm/train/cpt/embed_warmup.py" = ["ARG002"] # Modality dispatch uses a polymorphic interface — each subclass uses # a different subset of the keyword args (text.dispatch_export reads # none, VL reads gguf_emission_context, audio ignores it). ARG002 # flags the unused ones in each branch; the shared signature is the # point of the abstraction. "src/dlm/modality/*.py" = ["ARG002"] [tool.ruff.format] quote-style = "double" indent-style = "space" # -------- mypy -------- [tool.mypy] strict = true python_version = "3.11" packages = ["dlm"] mypy_path = "src" warn_return_any = true warn_unused_ignores = true warn_redundant_casts = true no_implicit_optional = true disallow_untyped_decorators = true plugins = ["pydantic.mypy"] [tool.pydantic-mypy] init_forbid_extra = true init_typed = true warn_required_dynamic_aliases = true # HF ecosystem packages (Sprint 06/07/09) ship without stubs or py.typed markers. # Sprint 07 modules touch them through narrow boundaries; annotations are explicit # at our boundary so the rest of the codebase remains strict. [[tool.mypy.overrides]] module = [ "datasets.*", "transformers.*", "peft.*", "trl.*", "huggingface_hub.*", "bitsandbytes.*", ] ignore_missing_imports = true # Treat calls into these stubless ecosystems as Any-typed; their # actual return types aren't reflected in stubs. disable_error_code = ["no-untyped-call"] # Optional runtime deps used behind feature gates may be absent or # ship without type metadata on some CI runners. Keep those imports # narrow and local in code, and suppress missing-stub noise here # instead of scattering environment-specific inline ignores. [[tool.mypy.overrides]] module = [ "soxr", "scipy", "scipy.*", "mlx_lm", "mlx_lm.*", ] ignore_missing_imports = true disable_error_code = ["no-untyped-call"] # -------- pytest -------- [tool.pytest.ini_options] testpaths = ["tests"] addopts = [ "-ra", "-m", "not slow and not gpu and not online and not vl and not audio and not ollama", ] markers = [ "slow: expensive; deselected by default", "gpu: requires CUDA; skipped on CPU/MPS runners", "online: touches the network; skipped in offline CI", "vl: vision-language; requires a GPU + VL HF weights; deselected by default", "audio: audio-language; requires a GPU + audio HF weights; deselected by default", "ollama: requires a local Ollama install (0.4+); deselected by default", ]