Python · 6062 bytes Raw Blame History
1 """Top-level Typer application.
2
3 Telemetry-off defaults are applied before any downstream imports.
4 Subcommand stubs live in `dlm.cli.commands`.
5 """
6
7 from __future__ import annotations
8
9 import os
10
11
12 def _disable_third_party_telemetry() -> None:
13 """Force opt-out env vars before third-party imports.
14
15 This is load-bearing for the "no telemetry, ever" promise. Must
16 run before `transformers`, `huggingface_hub`, `wandb`, or similar
17 are imported anywhere in the process.
18 """
19 os.environ.setdefault("HF_HUB_DISABLE_TELEMETRY", "1")
20 os.environ.setdefault("DO_NOT_TRACK", "1")
21 os.environ.setdefault("TRANSFORMERS_NO_ADVISORY_WARNINGS", "1")
22
23
24 _disable_third_party_telemetry()
25
26
27 # Imports below are deferred so `_disable_third_party_telemetry` runs first.
28 from typing import Annotated # noqa: E402
29
30 import typer # noqa: E402
31
32 from dlm import __version__ # noqa: E402
33 from dlm.cli import commands # noqa: E402
34
35 app = typer.Typer(
36 name="dlm",
37 help="DocumentLanguageModel — a text file becomes a local, trainable LLM.",
38 no_args_is_help=True,
39 rich_markup_mode="rich",
40 add_completion=True,
41 context_settings={"help_option_names": ["-h", "--help"]},
42 )
43
44
45 def _version_callback(value: bool) -> None:
46 if value:
47 typer.echo(f"dlm {__version__}")
48 raise typer.Exit(code=0)
49
50
51 @app.callback()
52 def _root(
53 version: Annotated[
54 bool,
55 typer.Option(
56 "--version",
57 callback=_version_callback,
58 is_eager=True,
59 help="Show version and exit.",
60 ),
61 ] = False,
62 home: Annotated[
63 str | None,
64 typer.Option("--home", envvar="DLM_HOME", help="Override $DLM_HOME (default ~/.dlm)."),
65 ] = None,
66 verbose: Annotated[bool, typer.Option("--verbose", "-v", help="Verbose logging.")] = False,
67 quiet: Annotated[
68 bool, typer.Option("--quiet", "-q", help="Suppress informational output.")
69 ] = False,
70 ) -> None:
71 """Root callback — configure DLM_HOME + logger level for downstream cmds."""
72 import logging
73
74 _ = version # consumed by is_eager callback before we arrive
75
76 if home is not None:
77 # Make the override visible to every `StorePath.for_dlm`
78 # downstream of this callback.
79 os.environ["DLM_HOME"] = home
80
81 if verbose and quiet:
82 raise typer.BadParameter("--verbose and --quiet are mutually exclusive")
83
84 level = logging.INFO
85 if verbose:
86 level = logging.DEBUG
87 elif quiet:
88 level = logging.WARNING
89
90 # Only attach a handler if the root logger hasn't already been
91 # configured by the parent process (e.g. in tests). `force=True`
92 # resets any prior CLI-run handlers so --verbose on a second call
93 # actually takes effect.
94 logging.basicConfig(
95 level=level,
96 format="%(asctime)s %(name)s %(levelname)s: %(message)s",
97 force=True,
98 )
99
100
101 # Register subcommand stubs.
102 app.command("init")(commands.init_cmd)
103 app.command("train")(commands.train_cmd)
104 app.command("prompt")(commands.prompt_cmd)
105 app.command("repl")(commands.repl_cmd)
106 app.command("export")(commands.export_cmd)
107 app.command("pack")(commands.pack_cmd)
108 app.command("unpack")(commands.unpack_cmd)
109 app.command("verify")(commands.verify_cmd)
110 app.command("push")(commands.push_cmd)
111 app.command("pull")(commands.pull_cmd)
112 app.command("serve")(commands.serve_cmd)
113 app.command("doctor")(commands.doctor_cmd)
114 app.command("show")(commands.show_cmd)
115 app.command("migrate")(commands.migrate_cmd)
116 app.command("harvest")(commands.harvest_cmd)
117
118 # `dlm synth instructions|preferences|revert|list` — synthetic data loop.
119 _synth_app = typer.Typer(
120 help="Synthesize instruction or preference training data.",
121 no_args_is_help=True,
122 )
123 _synth_app.command("instructions")(commands.synth_instructions_cmd)
124 _synth_app.command("preferences")(commands.preference_mine_cmd)
125 _synth_app.command("revert")(commands.synth_revert_cmd)
126 _synth_app.command("list")(commands.synth_list_cmd)
127 app.add_typer(_synth_app, name="synth")
128
129 # `dlm preference mine|apply|revert|list` — auto-mined preference loop.
130 _preference_app = typer.Typer(
131 help="Mine, stage, apply, and inspect auto-mined preference sections.",
132 no_args_is_help=True,
133 )
134 _preference_app.command("mine")(commands.preference_mine_cmd)
135 _preference_app.command("apply")(commands.preference_apply_cmd)
136 _preference_app.command("revert")(commands.preference_revert_cmd)
137 _preference_app.command("list")(commands.preference_list_cmd)
138 app.add_typer(_preference_app, name="preference")
139
140 # `dlm metrics <path>` + `dlm metrics watch <path>` as a subcommand
141 # group. Typer nests naturally via an Annotated sub-app.
142 _metrics_app = typer.Typer(
143 help="Query the per-store metrics database.",
144 no_args_is_help=True,
145 invoke_without_command=True,
146 )
147 _metrics_app.callback(invoke_without_command=True)(commands.metrics_cmd)
148 _metrics_app.command("watch")(commands.metrics_watch_cmd)
149 app.add_typer(_metrics_app, name="metrics")
150
151 # `dlm templates list` lives under its own subcommand group.
152 _templates_app = typer.Typer(
153 help="Browse the starter template gallery.",
154 no_args_is_help=True,
155 )
156 _templates_app.command("list")(commands.templates_list_cmd)
157 app.add_typer(_templates_app, name="templates")
158
159 # `dlm cache show|prune|clear` — per-store tokenized-section cache
160 # maintenance.
161 _cache_app = typer.Typer(
162 help="Inspect and manage the per-store tokenized-section cache.",
163 no_args_is_help=True,
164 )
165 _cache_app.command("show")(commands.cache_show_cmd)
166 _cache_app.command("prune")(commands.cache_prune_cmd)
167 _cache_app.command("clear")(commands.cache_clear_cmd)
168 app.add_typer(_cache_app, name="cache")
169
170
171 def main() -> None:
172 """Installed entry point (`dlm` on PATH).
173
174 Routes uncaught exceptions through `dlm.cli.reporter` so users see
175 a clean one-liner instead of a raw traceback; `--verbose` surfaces
176 the traceback for debugging.
177 """
178 import sys
179
180 from dlm.cli.reporter import run_with_reporter
181
182 sys.exit(run_with_reporter(app))
183
184
185 if __name__ == "__main__":
186 main()